首先想申明一下,我并不是明年毕业的应届生,写这些笔试题是为以后找工作做准备,所以我的代码并没有提交检测过,欢迎大家指点错误!题目为美团4月2号在牛客网的笔试,一共五道题,难度挺大的,除了那道动态规划其他题我都没能想到完全正确的解法,后来在这位大佬这里学习了一波,大佬的博客写的比较简单,我理解了很久然后全部写了一遍。

第一题 不能超过

给出一个序列包括n个正整数的序列A,从中删除几个数使得序列中最大值减去最小值小于等于x,请问最少删除几个数字。
输入第一行为n和x,表示序列的长度和x,1<=n<=1000,1<=x<=10000;
第二行是n个正整数a[i]。
输出一个正整数,表示最少删除的数字数量。
样例如下:
输入
5 2
2 1 3 2 5
输出
1

分析

这题因为n很小,复杂度O(n^2)是完全可以接受的,那么我们先排个序,然后遍历序列,以当前a[i]作为最小值,看序列中在[a[i], a[i]+x]范围内的有多少个数,比如说有sum个数,更新ans = min(ans, n-sum)。
评论里面很多人说只过了64%,可能测试数据有问题吧。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int x, n;
int num[1010];
int main()
{cin >> n >> x;for (int i = 0; i < n; i++) cin >> num[i];sort(num, num+n);int ans = n;for (int i = 0; i < n; i++){int sum = 0;for (int j = i; j < n; j++) if (num[j]-num[i] <= x)sum++;else break;if (n - sum < ans) ans = n - sum;}cout << ans << endl;return 0;
}

题目二

有n个房间成环状排列,1号房间连2号,2号连三号。。。n号连着1号,在第i个房间留下一个烙印需要花费a[i]点法力,小明初始位于1号房间,共有m点法力,小明很耿直,他到一个房间如果有足够的法力留下印记他就一定会花费a[i]点法力留下印记然后到下一个房间,法力不够的话就直接去下一个房间,当小明的法力值无法在任何房间留下印记的时候游戏就结束了,那他一共可以留下几个印记。
输入第一行包括n 和 m,表示房间数和初始法力值,1<=n<=100000,1<=m<=10^18;
第二行有n个正整数a[i],表示每个房间留下印记需要的法力值,
1<=a[i]<=10^9;
输出为一个整数,表示游戏结束时留下的印记数。
样例如下:
输入
4 21
2 1 4 3
输出
9
先把四个房间走一遍,有四个印记,剩余法力11,再走一遍,又有四个印记,剩余法力1,在第二个房间留下第九个印记,游戏结束。

分析

大佬的题解
说实话我不是很看的懂,所以又去学习了一下树状数组,大概明白了,树状数组是用来存储前缀和的,原数组a[i],前缀和数组我设为fro[i],把八个数字拿来举例:
fro[1] = a[1]
fro[2] = a[1] + a[2]
fro[3] = a[3]
fro[4] = a[1] + a[2] + a[3] + a[4]
fro[5] = a[5]
fro[6] = a[5] + a[6]
fro[7] = a[7]
fro[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]
首先我们需要知道x&(-x)是什么,x & -x 代表二进制表示下除最右一位 1 保留,其它位全部为 0, 例如6&(-6) 得到 2 00000110 & 11111010 = 00000010。
我们在构造树状数组时不断进行x += x&(-x),所以a[1]在fro[1]、fro[2]、fro[4]、fro[8]中都存在,a[3]除了出现在fro[3]中,还出现在fro[4]中,因为3的二进制是11,右边第一位就是1,11 + 1 = 100,而4的右边第一次出现1的位置是第三位,100 + 100 = 1000,所以fro[8]里也包含了a[3]。
我们获取前缀和的时候就用x -= x&(-x),比如我现在想得到前七项的和,7的二进制是111,sum += fro[7],111 - 001 = 110,sum += fro[6],110-010=100,sum+=fro[4],100-100=0,没得了。
那这题怎么搞呢,大佬的解析:维护一个sum(一圈下来的和),循环内,每次先把可以整圈的部分算了(除法取模);不能整圈(sum>m)的情况,显然某个前缀的sum已经>m了,所以我们二分这个前缀(动态前缀和使用树状数组维护),将第一个大于m的前缀的那个位置删掉。
太精彩了!!!但是需要花一点时间理解。直接举例来说明,比如这个样例,现在一圈需要10点法力值,有21点法力值,所以可以跑两圈也就是得到八个印记,剩下1点法力值小于一圈的10点,说明从某个房间开始就已经大于1点了,在这个样例2 1 4 3里,从第一个房间开始就大于1点了,那么把第一个房间去掉,怎么去掉呢?用update(i, -a[i])就可以了,这样就能把fro里所有有a[i]的位置都处理一遍,实际的效果是相当于把a[i]置0,当然,实际的a数组并没有变动,变动的只是fro数组。那么现在fro对应的a序列就成了0 1 4 3,一圈下来还是大于1点,那么再找一次第一个大于1的前缀,显然是第三个房间,现在a序列变成了0 1 0 3,一圈需要4点法力,还是大于1点,那再来一次,变成0 1 0 0,好了现在可以循环一圈了,法力值消耗完毕,游戏结束。第二种游戏结束的情况就是序列全为0了,这里为了方便获取结束条件设了一个res来保存还有几个不为0的数。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;int n;
long long m;
long long a[100010], fro[100010];//fro = Prefix sum of tree array
int lowbit(int x)
{return x&(-x);
}
void update(int pos, long long num)
{while (pos <= n){fro[pos] += num;pos += lowbit(pos);}
}
long long get_sum(int pos)
{long long sum = 0;while (pos > 0){sum += fro[pos];pos -= lowbit(pos);}return sum;
}
int main()
{cin >> n >> m;long long sum = 0;for (int i = 1; i <= n; i++){cin >> a[i];update(i, a[i]);sum += a[i];}int res = n;long long power = m;long long ans = 0;while (res && power){long long cnt = power / sum;power %= sum;ans += cnt * res;int L = 1, R = n;while (L < R){int mid = (L + R) / 2;if (get_sum(mid) > power)R = mid;else L = mid + 1;}update(L, -a[L]);res--;sum -= a[L];}cout << ans << endl;return 0;
}

题目三

小仓喜欢射击,初始有N颗子弹,小仓自由选择k颗子弹进行射击,命中的概率为p[k],命中的话获得a[k]的分,然后可以使用剩下的子弹继续进行射击,没有命中的话今天的游戏结束,小仓能获得的得分最大期望是多少。
第一行为一个数N,表示子弹数量。
第二行N个数p[i],表示一次性用i颗子弹命中的概率。
第三行N个数a[i],表示一次性用i颗子弹命中以后可以获得的分数。
1<=N<=5000 0 <= p[i] <= 1 0<=a[i]<=1000
输出一个最高期望得分,保留两位小数。
样例如下:
输入1
2
0.8 0.5
1 2
输出1
1.44
输入2
3
0.9 0.1 0.1
2 1 1
输出2
4.88
解释:样例一中选择一颗子弹,如果命中再继续用一颗,期望是0.8*1+0.8*0.8*1 = 1.44,如果选两颗子弹一起射出,期望是0.5*2=1,所以最大期望是1.44;
样例二,如果直接用三颗,期望是0.1*1,如果先用两颗再用一颗,是0.1*1+0.1*0.9*2,如果先用一颗再用两颗,是0.9*2+0.9*0.1*1,一颗一颗又一颗,是0.9*2+0.9*0.9*2+0.9*0.9*0.9*2

分析

有点被样例影响,开始我以为只能选择k颗然后一直用下去,那么直接遍历就好了,后来细读题目才知道是动态规划问题,现在用了k颗,获得的分数期望是p[k]*a[k],那么下一次初始就是N-k颗,并且需要成功才能继续,那就是
dp[N] = p[k]*a[k] + p[k]*dp[N-k],这里的k显然从1到N取一个能使dp[N]最大的,还是比较容易想到的。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int N;
int a[5010];
double p[5010], dp[5010];
int main()
{cin >> N;for (int i = 1; i <= N; i++) cin >> p[i];for (int i = 1; i <= N; i++)  cin >> a[i];memset(dp, 0, sizeof dp);for (int i = 1; i <= N; i++)for (int j = 1; j <= i; j++)dp[i] = max(dp[i], dp[i-j]*p[j] + a[j]*p[j]);printf("%.2f", dp[N]);return 0;
}

题目四

给一个字符串s和一个正整数k,s仅包含小写字母,问s有多少个ABA型的子串,这里A的长度大于等于k,B的长度大于等于1。
输入第一行为s,第二行为k。
输出个数。
样例:
输入
abcabcabc
2
输出
8
解释:
abcab [1, 5]
abcabcab [1, 8]
abcabcabc [1, 9]
bcabc [2, 6]
bcabcabc [2, 9]
cabca [3, 7]
abcab [4, 8]
bcabc [5, 9]

分析

乍一眼以为是kmp,写了一发然后GG,不太好搞,还是看了大佬的博客,然后学习了一下字符串哈希化,看了一篇写的很好的博客可是找不到了,我把我的理解整理如下:
字符串哈希化就是把字符串变成一个整数,这样比较两个字符串是否相等的时候就只需要比较他俩对应那个整数是否相同,节约了判断的复杂度,我们当然可以把字符串所有子串都存进一个unordered_map<string, long long> 里,但是构造所有子串工作量太大,不够优雅,所以选择哈希化。首先需要准备一个p,一个mod,例如我现在有字符串“abcda",那么

hash[1] = 0*p + 'a'-'a' = 0;      0对应子串"a"
hash[2] = hash[1]*p + 'b'-'a' = 1;   1对应子串"ab"
hash[3] = p + 2;                  对应"abc"
hash[4] = p*(p+2) + 3;               对应"abcd"
hash[5] = p^3 + 2*p^2 + 3*p;         对应"abcda"

现在我想获取子串[2, 3]即"bc"的值,那就用hash[3]-hash[2-1]*p^(3-2+1)
子串[5, 5],hash[5]-hash[4]*p^(5-5+1) = 0和字串[0, 0]相等。
理论研究我也整不太明白,反正p取一个质数,比如103、131、163、233等,然后因为字符串转成的数字会很大所以取余,mod可以选择1e9+7这样的质数,那么我们在计算hash值过程中要非常注意取余,我打算就用大佬的这个方法:

LL getHash(int l,int r){return (has[r]-has[l-1]*bas[r-l+1]%mod+mod)%mod;
}

这里bas数组是预处理出来的p^(r-l+1),因为减法有可能出现负数但是负数不能取余所以+mod。
这样将字符串进行哈希化以后就可以很方便的得知[1, k]子串是否等于[j, j+k]子串了,也就是很容易判断是否是ABA型。
那么我们遍历i和j查看是否有[i, i+k-1] = [j, j+k-1]就能知道是否是ABA型,当前的j满足这个等号后,还需要继续查看[i, i+k+cnt] 是否等于 [j, j+k+cnt],i+k+cnt小于j-1,j+k+cnt小于n,这样算下来我们会有[i, j+k-1]~[i, j+k+cnt]这些符合条件的子串。但是,这样获得的子串会有重复,例如:
aaaaaaaaa
当i为1, j 为5时,此时[1,2]=[5,6],继续查看发现[1, 3]=[5, 7]此时获得的ABA子串是[1, 6]和[1, 7];
而当i为1, j为6时,此时[1,2]=[6,7],获得的ABA子串是[1, 7];
重复了!所以对于一个i,可行的ABA子串为[i,j+k-1]~ [i , j+k+cnt],我们把每一个j对应的[j+k-1, j+k+cnt]放进vector中排序进行区间合并,然后计算去重后的ABA子串数量。
不知不觉把自己绕进去了。。。还是看代码吧。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int p = 163;
const long long mod = 1e9+7;string s;
int k;
long long base[2010];//p的i次方
long long hval[2010];//字符串[1,i]对应的hash值
long long getHash(int L, int R)
{return (hval[R] - hval[L-1]*base[R-L+1]%mod + mod) % mod;
}
int main()
{cin >> s;base[0] = 1;hval[0] = 0;for (int i = 0; i < s.size(); i++){base[i+1] = (base[i] * p) % mod;hval[i+1] = (hval[i] * p + (s[i] - 'a')) % mod;}cin >> k;int n = s.size();int ans = 0;for (int i = 0; i < n-2*k; i++){//保证子串长度大于等于2*k+1vector<pair<int, int> > v;for (int j = i+k+1; j < n-k+1; j++){//j与i的距离大于等于k+1, j距离字符串尾至少有k个字符int cnt = 0;while (j+k+cnt <= n && i+k+cnt <= j-1 && getHash(i+1, i+k+cnt) == getHash(j+1,  j+k+cnt)){cnt++;}if (cnt){//如果匹配上了cnt--;v.push_back(make_pair(j+k, j+k+cnt));//cout << i+1 << " " << i+k+cnt << " " << j+k << " " << j+k+cnt << endl;}}if (v.empty()) continue;sort(v.begin(), v.end());//排序int L = v[0].first, R = v[0].second;for (int i = 1; i < v.size(); i++)if (v[i].first > R){ans += R-L+1;L = v[i].first;R = v[i].second;}else R = max(R, v[i].second); //去重ans += R-L+1;}cout << ans << endl;return 0;
}

题目五 max xor min

给一个长度为n的序列,求出1<=L<R<=n的区间中最大值和最小值的异或和的异或和(这像人话吗?!)
其实意思就是把序列的所有长度大于1的连续子序列中max xor min的值全部异或起来。
输入第一行是一个正整数n,1 <= n <= 10^5;
第二行是n个正整数a[i], 1<=a[i]<=10^9;
输出一个整数。
样例:
输入
3
1 3 5
输出
0
解释:子序列有1 3, 1 3 5, 3 5三个,那么(1^3)^(1^5)^(3^5) = 0

分析

我的想法跟之前阿里第二题有点相似,先搞出当前num[i]作为最大值的左边界i-L和右边界i+R来,这里因为子序列长度大于1,那么以num[i]作为最大值的子序列有L*R-1个,因为异或有x^x=0这个性质,所以判断一下奇偶,如果有奇数个,就需要ans^=num,然后最小值也这样处理。大佬的博客写的是对于每个点,从前往后和从后往前做两遍单调栈,处理出每个点作为最大值的范围[L,R]单调栈是啥?我好像又不会。。。
单调栈看这里!
还是很简单的呀,就拿一个栈搞来搞去,然后发现其实我之前写过单调栈的题只是看了题解没记住单调栈这个名字和方法,还是要多总结。我顺便改进了一下,在栈中先放置一个-1,这样就很方便直接令L[i] = sta.top()+1,不需要另外写栈空的时候L[i] = 0。
所以写完这篇博客我还可以去改一改之前阿里第二题的代码。

代码

看起来长但都是复制粘贴改一改。左边允许等于右边不允许

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;int n, L[100010], R[100010];
long long a[100010];
int main()
{cin >> n;for (int i = 0; i < n; i++) cin >> a[i];stack<int> sta;sta.push(-1);//求以a[i]为最大值的L[i]for (int i = 0; i < n; i++){if (sta.top()==-1 || a[sta.top()] > a[i]){L[i] = sta.top()+1;sta.push(i);}else {while (sta.top()!=-1 && a[sta.top()] <= a[i]){sta.pop();}L[i] = sta.top()+1;sta.push(i);}}while (!sta.empty()) sta.pop();sta.push(n);//求以a[i]为最大值的R[i]for (int i = n-1; i >= 0; i--){if (sta.top()==n || a[sta.top()] >= a[i]){R[i] = sta.top()-1;sta.push(i);}else {while (sta.top()!=n && a[sta.top()] < a[i]){sta.pop();}R[i] = sta.top()-1;sta.push(i);}}long long ans = 0;for (int i = 0; i < n; i++)if ((i-L[i]+1)%2==0 || (R[i]-i+1)%2==0)ans ^= a[i];while (!sta.empty()) sta.pop();sta.push(-1);求以a[i]为最小值的L[i]for (int i = 0; i < n; i++){if (sta.top()==-1 || a[sta.top()] < a[i]){L[i] = sta.top()+1;sta.push(i);}else {while (sta.top()!=-1 && a[sta.top()] >= a[i]){sta.pop();}L[i] = sta.top()+1;sta.push(i);}}while (!sta.empty()) sta.pop();sta.push(n);求以a[i]为最小值的R[i]for (int i = n-1; i >= 0; i--){if (sta.top()==n || a[sta.top()] <= a[i]){R[i] = sta.top()-1;sta.push(i);}else {while (sta.top()!=n && a[sta.top()] > a[i]){sta.pop();}R[i] = sta.top()-1;sta.push(i);}}for (int i = 0; i < n; i++)if ((i-L[i]+1)%2==0 || (R[i]-i+1)%2==0)ans ^= a[i];cout << ans << endl;return 0;
}

感谢文中所有链接的博主!
怕什么真理无穷,进一步有一步的欢喜!

2020美团实习生招聘笔试题相关推荐

  1. 2020阿里实习生招聘笔试题

    题目1 小强有n个养鸡场,第i个养鸡场初始有a[i]只小鸡,小强的每个养鸡场每天早上都会增加k只小鸡,到了下午小强会把鸡最多的鸡场卖掉一半鸡,那么小强想知道m天后他所有养鸡场一共有几只鸡. 第一行输入 ...

  2. 网易2018实习生招聘笔试题-JAVA开发实习生

    网易2018实习生招聘笔试题-JAVA开发实习生 如何从有数字规律的网址抓取网页并保存在当前目录?假设网址为 http://test/0.xml,其中这个数字可以递增到100. for(int i=0 ...

  3. 前端实习生笔试_2017携程Web前端实习生招聘笔试题总结

    考察encodeURI encodeURI(), decodeURI()它们都是Global对象的方法. encodeURI()通过将某些字符的每个实例替换代表字符的UTF-8编码的一个或多个转义字符 ...

  4. 数据分析真题日刷 | 网易2018实习生招聘笔试题-数据分析实习生

    上周开始实习,博客更新就缓下来了.这是十天前做的套题了,现在拾起来把它再整理整理. 网易数据分析实习生的笔试题,和校招的题目还是有部分重复的,不过难度也不小. 今日真题 网易2018实习生招聘笔试题- ...

  5. 【牛客】网易2018实习生招聘笔试题——数据分析师实习生解析

    [牛客]网易2018实习生招聘笔试题--数据分析师实习生解析 * 选择题根据牛客网下方讨论整理,三道大题均为自己答案,欢迎大家讨论并给予指正. (https://www.nowcoder.com/te ...

  6. 前端实习生笔试_百度实习生招聘笔试题-web前端开发

    百度的每个职位笔试题都不一样,我做的是 web 前端开发的题 一.简答题 1 . A . prototype.name 是什么含义,用来解决什么问题? 2 . javascript 的函数题,考一下应 ...

  7. 亚信科技2019届实习生招聘笔试题(前端)

    亚信来我们学校宣讲,让我们做了笔试题,我选择了前端方向,下面是我的试题整理还有我的答案(不一定正确)以及知识点整理,如果有错误请大神们指正.这份笔试题涉及到了HTML CSS JavaScript j ...

  8. 2014年实习生招聘之华为实习生招聘笔试题(上机完成)—2014/04/02

    作者:Bryant Lei 出处:http://blog.csdn.net/bryantlei 华为(武汉)今年的实习生招聘的第一轮选拔是上机测试,其平台类似于常见的编程网站,即用户提交程序,由系统测 ...

  9. 网易2018实习生招聘笔试题的收获

    1.在二叉树的遍历是出错(时间久了忘记的差不多) 顺便复习一下3种遍历: 先序遍历:遍历规则(中左右) 中序遍历:遍历规则(左中右) 后序遍历:遍历规则(左右中) 先序:ABCDEFGHK 中序:BC ...

  10. 【笔试or面试】UC2014实习生招聘笔试题

    今晚参加了UC的实习生笔试,本着练练的心态去参加,还是被虐了,还是因为基础不扎实啊.在这里总结一下笔试的题,为后来的笔试和面试做下积累. 1.首先总结下最后一道编程题,当时思考了挺久的.给出一个整数数 ...

最新文章

  1. 黑科技,教你用Python打电话,控制手机技术,快来学一下
  2. [转]int.ToString()输出不同格式的数字字符串
  3. oracle将213变成123,oracle 转换函数
  4. javaWeb_JSP 动态指令 forward 的程序
  5. 【Python教程】七种创建对象的方式,你知道几种?
  6. 排查一个触摸屏驱动问题
  7. 复杂电网三相短路计算的matlab仿真,复杂电网三相短路计算的MATLAB仿真电力系统分析课设报告 - 图文...
  8. 信息学奥赛一本通 1942:【08NOIP普及组】ISBN号码 | OpenJudge NOI 1.7 29:ISBN号码 | 洛谷 P1055 [NOIP2008 普及组] ISBN 号码
  9. 根据接口文档中的入参,生成自动化测试用例中的异常测试用例,包含用例描述,用例数据
  10. 数据结构与算法(js)
  11. MySQL数据库导入错误:ERROR 1064 (42000) 和 ERROR at line xx: Unknown command '\Z'.
  12. 查看计算机安装程序版本,Product Key Explorer(程序密钥显示工具)
  13. 【千锋】网络安全笔记(总篇)--- 持续更新
  14. 某宝抢购taobaosnap开发与实现
  15. Google原生输入法LatinIME词库构建流程分析(一)
  16. 生物计算机是未来型计算机吗,生物计算机特点及未来发展
  17. Symbol Factory Universal v3.X 工业图形库
  18. csdn前200的大牛
  19. mysql 时区时间_mysql的时间不对(时区问题)
  20. java hotspot server_Java HotSpot(TM)64位服务器VM警告

热门文章

  1. 为Exynos4412移植2022版U-Boot(一)步骤及其原理分析
  2. 企业信息化将推动企业组织结构和管理模式的变革
  3. 管道—过滤器简介 软件体系结构
  4. java字符串去空格、制表符、换页符
  5. 数据可视化 Echarts + 边框图片 + ES6拼接字符串
  6. 【转】货币的未来取决于打破关于货币历史的虚构谎言
  7. cpp中string类
  8. 【OpenCV】图像进行数字化操作:像素确定位置、获取像素BGR值、修改像素BGR值、修改指定区域内像素
  9. 计算机重启是什么原因,电脑自动重启是什么原因以及如何解决【图文教程】
  10. html表格边框格式,html设置表格边框样式的方法