拼多多笔试 第三批 3.1 更新了第四题答案

#写了第四题答案,但线下造的例子过了,线上不知道
#直接拉最下面就可以看了


'''
算法工程师方向,和后端、前端题目一样。由于我肯定去不了拼多多了所以直接屏幕截图了,估计被判作弊了。很简单,算法工程师都知道,检测视频频谱最高的部分抽帧,直接就能检测到打开word并复制截图的关键帧,这种抽帧作弊检测很好写,我不相信牛客没有这技术。但无所谓了
或者检测Ptr键也行。
总共花了不到一小时,但是最后一题无思路,就交了,求一波第四题答案。
'''
求求求求求求求第四题思路。
题1:不等式推导
n = int(input())
for _ in range(n):
    a,b,c = list(map(int,input().split()))
    res = min((a+b+c)//3,min(a,b))
    print(res)

思路:成团要至少有一个a,b。那么输出的结果一定有res<=min(a,b)
然后成团要配三个,那么去掉用来配对的a或b后还要有两个备胎,即
sum - min(a,b) 和 2*min(a,b)比较,如果去掉配对的sum - min(a,b) 还有很多,那么只用min(a,b)输出就行了。
但不够配对了,那么就要输出(sum - res)//2了。
所以结果
res<=(sum - res)//2,即res<=sum//3
res<=a
res<=b
因此res = min{sum//3,a,b};
python //是整除号,C++直接/就行。

题2:动态规划
发现最小值周期是4就简单了,所谓的绝对值减过来减过去无非就是数组选一部分减另一部分的绝对值。
dp1 = [0,1,1,0]
def get_min(n):
    if n<=0:
        return 0
    else:
        return dp1[n%4]
def get_max(n):
    return n - get_min(n-1)
T = int(input())
for _ in range(T):
    n = int(input())
    print(get_min(n),get_max(n))

首先一定要得知道F(N)代表了1-N这N个数选一部分减去另一部分的绝对值。
最小值如何算呢?我一开始以为是奇偶关系,发现不是,无规律。然后突然注意到四个数可以配出来0。举个例子,1 2 3 4 5 6 7。
4 5 6 7是能凑出来0的,那么5 6 7 8啊 8 9 10 11都可以凑出来0,则4是周期。
N=1的时候只要1个数,min=1。
N=2,min=2-1=1。
N=3,min=3-2-1=0。示例
N=4,根据周期性得出N=4等于N=0,min=1+4-2-3=0。
最大值等于 最后一个数 -(前面N-1个数的最小值) 且最后一个数一定是N,因为最小值值域是0,1,这种方法得出来的结果一定是N或者N-1。无论其他情况怎么配,只要最后一个数不比N-1大,那么它减去一个绝对值一定是<=N-1的,不会比这种配法得到的最大值要大。
题3:动态规划,不同上升子序列的个数
n = int(input())
data = list(map(int,input().split()))
c_data = sorted(list(set(data)))
dp = [0]*n
dpi = [0]*n
for val in data:
    index = c_data.index(val)
    dpi[index]=1
    cur = sum(dpi[:index]) + sum(dp[:index])
    dp[index] = cur
print(sum(dp))

动态规划*2,不同的上升子序列的数目。
我是把上升子序列分成了两种情况,第一种是配出序列长度为2的子序列,配法是读出当前的值,判断有多少个值小于他,那么就能配出来多少个长度为2的子序列。dpi对应各个val的个数,取值0,1。
另一种情况是长度大于3的,这里记录了所有以val结尾的子序列长度>=2的数目,则对于新来的数,以他结尾的大于2的子序列数目为:判断有多少个值小于他,把这些值结尾的子序列数目加起来即可。
最后对于每个新值,其子序列数目为长度为2的部分加上长度大于2的部分。
由于要求去重,所以用了set()。
题4:不会 暴力10%
一个数组,得分是任选两个数,他们的和与他们的距离定义为得分。距离是圆周距离。
换成C/C++写依旧是10%
暴力走了一遍,无思路。
翻译一下:一个数组,取两个数,算他们的距离与值的和。
距离为圆周距离,好比1,2,3。1,3的距离是1,因为他们挨着。
可能关于手串的项链啊什么的动态规划有类似的题吧,还是题刷少了,但连续三题动态规划吗。。。
n = int(input())
data = list(map(int,input().split()))
res = 0
cur_index = 0
for i in range(n):
    for j in range(i+1,n):
        tmp = min((i-j+n)%n,j-i) + data[i]+data[j]
        if tmp>res:
            res =tmp
            cur_index = 0
        elif tmp==res:
            cur_index+=1
print(res,cur_index+1)

最后FPX加油!


#########################################################################
第四题:简单扫描模拟
一个数组,得分是任选两个数,他们的和与他们的距离定义为得分。距离是圆周距离。
返回最大得分,和最大得分的情况个数。
每次进来一个新的数,选择前面的最大值和他配对
优弧:data[j]+j+(data[i]-i)
劣弧:data[j]-j+(data[i]+n+i)
优弧存(data[i]-i),最大值max1,劣弧存(data[i]+n+i),最大值max2。优弧得分为data[j]+j+max1,劣弧得分为data[j]-j+max2
题干又臭又长,但是题意非常之简单,选择数组两个数,他们的得分是各自的和加上距离。
距离是圆周上的距离,举个例子就知道了
数组是 1 2 3 4 5 6。
1和2的距离是1,1和3的距离是2,1和4的距离是3,1和5的距离是2,1和6的距离是1。
问数组最大得分,和取到最大得分的情况数目。
(默认j>i)
如果直接是距离,这题就很简单很简单,直接把data[i]-i存起来读data[i]-i的最大就行了。然后对新数据j,判断data[j]+j-data[i]-i有没有超过最大值,超过就更新。然后再把data[j]-j存起来。
这个题麻烦就麻烦在距离变了,当时还有一小时,我是一点思路也没有,急着看比赛就交了。事后和评论讨论了一下,写了一版代码出来。
思路就是:
建立两个map存数据,一个存优弧,一个存劣弧。这是因为优弧和劣弧的得分计算方式不一样。
优弧 data[j]+data[j]+data[i]-i
劣弧 data[j]-j+data[i]+n+i
所以优弧存的是data[i]-i,劣弧存的是data[i]+n+i。对于过半的数据,每次把data[i]-i的数据弹出来放在劣弧里就行了。
这里由于数据很大1e6,所以只能用C/C++写,不要用iostream,太慢了。然后划数据放在了栈里面,因为栈开辟很快。
但是存数据有两种,一种是红黑树,读数据最坏复杂度O(nlog2n)。一种是哈希,读数据是O(1)。考虑到这样一种情况,因为要很快的读出来最大值,哈希表万一退化到O(n)就凉凉了,所以用的红黑树。对应C++的map。
为了能够更快的找到最大值,这里用了延迟更新。因为找最大值这一步是在弹出数据这一步,如果最大值全弹出了就要更新,所以每次弹出数据的时候记录个数-1,但只在最大值的个数<0的时候更新。
现在想想也没那么难。。。。。。
但是STL总归在堆里开辟,是比较慢的,但是没办法,总不能手写带删除的红黑树吧,过分了。
n = 1的时候,返回0 0吧,不知道题目判例有没有写这种恶心的特殊情况,写了就再特判下。
#include <stdio.h>
#include <map>
using namespace std;
inline int get_maxkey(map<int,int,greater<int>>&m)
{
    return m.begin()->first;
}
inline int get_maxnums(map<int,int,greater<int>>&m)
{
    return m.begin()->second;
}
int main()
{
    int n;scanf("%d",&n);
    int data[100005];//开辟的快点,不默认初值
    for(int i=0;i<n;++i)
        scanf("%d",&data[i]);
    //C++的map是红黑树,时间复杂度最坏O(log2n),找到最大值O(logn)
    int mid_index = (n+1)/2;
    map<int,int,greater<int>>data_i1;
    map<int,int>data_i2;
    int data_max1 = -0x3f3f3f3f;
    int data_nums1 = 0;
    int data_max2 = -0x3f3f3f3f;
    int data_nums2 = 0;
    int res_max = -0x3f3f3f3f;
    int res_nums = 0;
    for(int i=0;i<n/2;++i)
    {
        int cur_max = data[i]+i+data_max1;
        //结果最大值更新
        if(cur_max>res_max)
        {
            res_max = cur_max;res_nums=data_nums1;
        }else if(cur_max==res_max)
        {
            res_nums+=data_nums1;
        }
        //数据最大值更新
        if(data[i]-i>data_max1)
        {
            data_max1 = data[i]-i;data_nums1=1;
        }else if(data[i]-i==data_max1)
        {
            ++data_nums1;
        }
        //数据插入
        if(data_i1.count(data[i]-i))
        {
            ++data_i1[data[i]-i];
        }else
        {
            data_i1[data[i]-i]=1;
        }
        //printf("insert:%d\n",data[i]-i);
    }
    //开始存在劣弧,方法就是每次从优弧中弹出变成劣弧的数据,把劣弧转成优弧存起来。
    for(int i=mid_index;i<n;++i)
    {
        //先弹出劣弧
        int delete_i = i-mid_index;
        int delete_key = data[delete_i]-delete_i;
        data_i1[delete_key]-=1;
        if(delete_key==data_max1)
        {
            data_nums1-=1;
            if(data_nums1==0)
            {
                //最大值被删掉了,要重找最大值
                while(get_maxnums(data_i1)==0)
                {
                    data_i1.erase(get_maxkey(data_i1));
                }
                data_max1 = get_maxkey(data_i1);
                data_nums1 = get_maxnums(data_i1);
            }
        }
        //插入优弧,数据最大值更新
        if(data[delete_i]+n+delete_i>data_max2)
        {
            data_max2 = data[delete_i]+n+delete_i;data_nums2=1;
        }else if(data[delete_i]+n+delete_i==data_max2)
        {
            ++data_nums2;
        }
        if(data_i2.count(data[delete_i]+n+delete_i))
        {
            ++data_i2[data[delete_i]+n+delete_i];
        }else
        {
            data_i2[data[delete_i]+n+delete_i]=1;
        }
        //劣弧已更新,更新最大值,原优弧区
        int cur_max = data[i]+i+data_max1;
        if(cur_max>res_max)
        {
            res_max = cur_max;res_nums=data_nums1;
        }else if(cur_max==res_max)
        {
            res_nums+=data_nums1;
        }
        //新优弧区,即劣弧区
        cur_max = data[i]-i+data_max2;
        if(cur_max>res_max)
        {
            res_max = cur_max;res_nums=data_nums2;
        }else if(cur_max==res_max)
        {
            res_nums+=data_nums2;
        }
        //数据插入
        if(data_i1.count(data[i]-i))
        {
            ++data_i1[data[i]-i];
        }else
        {
            data_i1[data[i]-i]=1;
        }
        //printf("insert:%d\n",data[i]-i);
    }
    //输出结果
    printf("%d %d",res_max,res_nums);
    return 0;
}
#拼多多##笔经##笔试题目#
全部评论
前排
点赞 回复
分享
发布于 2019-11-10 20:35
膜拜大佬 服务端开发,一共四题,第一题10分钟搞定,后面三题挣扎者做了一个然鹅0%,直接交卷了,可能真的太久没刷题了😭
点赞 回复
分享
发布于 2019-11-10 20:40
阅文集团
校招火热招聘中
官网直投
没收到笔试消息,是投晚了吗
点赞 回复
分享
发布于 2019-11-10 20:41
来看看。
点赞 回复
分享
发布于 2019-11-10 20:45
等一波
点赞 回复
分享
发布于 2019-11-10 20:49
等大佬
点赞 回复
分享
发布于 2019-11-10 20:51
第四题是啥
点赞 回复
分享
发布于 2019-11-10 20:52
第一题,有人用java写通过了的吗?坐等观摩大佬代码
点赞 回复
分享
发布于 2019-11-10 21:25
求第三题思路?
点赞 回复
分享
发布于 2019-11-10 21:34
屏幕截图会被判作弊?为啥呀
点赞 回复
分享
发布于 2019-11-10 22:56
我觉得第三题题干写的有问题,测试用例和题干有些不符,后面在线问了老师,最后一道题双向指针吧
点赞 回复
分享
发布于 2019-11-10 23:35
为啥楼主去不了pdd?
点赞 回复
分享
发布于 2019-11-11 09:53
第三题,我用组合做的。先用set()去除重复的元素,然后求从剩下的数中大于等于2个元素的组合。用的是公式:从n个数中取m个数的组合,m的值从2开始取,一直取到n。 最后只有10%的case通过了。想了半天,不知道这个方法有什么问题。哪位大佬说说这个方法有哪些不合适的地方,教教我 QAQ
点赞 回复
分享
发布于 2019-11-11 12:03
作弊不会直接被pdd拉黑嘛
点赞 回复
分享
发布于 2020-04-28 14:31

相关推荐

6 62 评论
分享
牛客网
牛客企业服务