【360】2019校招笔试编程题合集
u1s1,好难。。两个小时根本做不完。最后找第三题的bug找到凌晨四点才过。
试题链接:2019校招笔试编程题合集
1、城市修建
题意:有一个城市需要修建,给你N个民居的坐标X,Y,问把这么多民居全都包进城市的话,城市所需最小面积是多少(注意,城市为平行于坐标轴的正方形)
题解:记得看题,是正方形!!!。先开始一直没过以为是长方形来着。。
记录输入的最大x,y和最小小x,y。然后相减,比较减出来最大的那个就是所求边长了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int inf = 1e9*5; 5 6 7 int main(){ 8 int n; 9 cin>>n; 10 ll x,y; 11 ll x1 = inf,y1 = inf; 12 ll x2 = -inf,y2 = -inf; 13 for(int i = 0 ;i < n;i++){ 14 cin>>x>>y; 15 x1 = min(x,x1); 16 y1 = min(y,y1); 17 x2 = max(x,x2); 18 y2 = max(y,y2); 19 } 20 21 ll width = abs(x2-x1); 22 ll height = abs(y2-y1); 23 24 ll ans = max(width,height); 25 26 cout<<ans*ans<<endl; 27 28 return 0; 29 }
2、圈地运动
题意:圈地运动,就是用很多木棍摆在地上组成一个面积大于0的多边形~
小明喜欢圈地运动,于是他需要去小红店里面买一些木棍,期望圈出一块地来。小红想挑战一下小明,所以给小明设置了一些障碍。障碍分别是:
1.如果小明要买第i块木棍的话,他就必须把前i-1块木棍都买下来。
2.买了的木棍都必须用在圈地运动中。
那么请问小明最少买多少根木棍,才能使得木棍围成的图形是个面积大于0多边形呢?
题解:这个本质是判断多边形的组成条件。构成m条边的多边形的条件,任意的m-1条边之和必须大于第m条边。
我们根据这个性质来做判断,当前最大值的边是否小于其他边的和。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int inf = 1e9; 5 6 int main(){ 7 int n; 8 cin>>n; 9 int cursum = 0; 10 int maxlen = 0; 11 int x; 12 13 if(n < 3) cout<<-1<<endl; 14 else{ 15 int cnt = 1; 16 int flag = 0; 17 cin>>x; 18 maxlen = x; 19 for(int i = 1; i < n ;i++){ 20 cin>>x; 21 cnt++; 22 if(x > maxlen){ 23 cursum += maxlen; 24 maxlen = x; 25 } 26 else cursum += x; 27 28 if(cursum > maxlen){ 29 flag = 1; 30 break; 31 } 32 33 } 34 if(flag) cout<<cnt<<endl; 35 else cout<<-1<<endl; 36 37 } 38 39 40 return 0; 41 }
3、Bittttttts
题意:现在有q个询问,每次询问想问你在[l,r]区间内,k进制表示中,k-1的数量最多的数是哪个数。比如当k=2时,9的二进制就是1001,那么他就有2个1.
题解:这题真的想了我好久。。本来准备用线段树做离线操作。后来发现1e16还是太大了。最后经韬韬点拨,发现可以这样思考。
[l,r]这个区间我们不需要完全知道k进制以后的所有。
- 首先需要比较l,r转化成k进制以后的数。补齐l的位数(因为l位数可能<=r的位数)
- 然后从高位开始比较l,r每一位,如果当前位不相同,记录一下
- 我们就从下一位开始将l的每一位变成k-1。(高位是根据l来判断的。因为所得数一定要在[l,r])
- 考虑特殊情况(在下面讲解)
- 转成十进制输出
比如: 8进制下 1和100. 1->1 100->144
首先补齐位数,001和144。从高位开始比较。可以得到最大的数是077。再转成十进制输出。
不明白的话再举一个例子:10进制下, 987666666和987566666
987666666
987566666 那么我们转换的最大k-1的数是 987599999
大概思路就是这样,里面的细节需要注意,下面就是特殊情况。
1、当r全是k-1时,我们要输出r。比如10进制下11111 99999 ,此时应该输出99999
2、当r从和l不等的这个pos这里全是k-1时,输出r。比如10进制下 1234567 1234999,此时输出1234999(这个情况想了好久,后面看到讨论区大佬给的数据才恍然大悟!!!!!)
具体的看一下代码,代码里给了注释。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int inf = 1e9; 5 6 ll maxnum = 0; 7 ll ans = 0; 8 9 vector<int> left_num; 10 vector<int> right_num; 11 12 //判断右边是不是全是k-1 13 bool all_r(int k){ 14 int len = right_num.size(); 15 for(int i = 0 ;i < len;i++){ 16 if(right_num[i] != k-1){ 17 return false; 18 } 19 } 20 return true; 21 22 } 23 24 ll query(int k,ll l, ll r){ 25 ll res = r; 26 while(l){ 27 left_num.push_back(l%k); 28 l /= k; 29 } 30 while(r){ 31 right_num.push_back(r%k); 32 r /= k; 33 } 34 //全为k-1的情况 35 if(all_r(k)) return res; 36 37 int len = right_num.size(); 38 int len1 = len - left_num.size(); 39 40 while(len1--) left_num.push_back(0); 41 42 ll ans=0; 43 int pos = 0; 44 for(int i = len-1 ; i >= 0 ;i--){ 45 if(left_num[i] != right_num[i]){ 46 pos = i; 47 break; 48 } 49 } 50 //cout<<pos<<endl; 51 52 int flag = 1; 53 for(int i = pos ;i >= 0;i--){ 54 if(right_num[i] != k-1){ 55 flag = 0; 56 break; 57 } 58 } 59 if(flag) return res; 60 //[len-1,pos]按l 61 for(int i = len-1; i >= pos ;i--) 62 ans = ans*k + left_num[i]; 63 64 //[pos+1,0]按k-1 65 for(int i = pos-1; i >= 0 ;i--) 66 ans = ans*k + k-1; 67 68 return ans; 69 } 70 int main(){ 71 int q; 72 cin>>q; 73 int k; 74 ll l,r; 75 while(q--){ 76 cin>>k>>l>>r; 77 left_num.clear(); 78 right_num.clear(); 79 cout<<query(k,l,r)<<endl; 80 } 81 82 83 return 0; 84 }
4、看花
题意:小明有一个花园,花园里面一共有m朵花,对于每一朵花,都是不一样的,小明用1~m中的一个整数表示每一朵花。
他很喜欢去看这些花,有一天他看了n次,并将n次他看花的种类是什么按照时间顺序记录下来。
记录用a[i]表示,表示第i次他看了a[i]这朵花。
小红很好奇,她有Q个问题,问[l,r]的时间内,小明一共看了多少朵不同的花儿,小明因为在忙着欣赏他的花儿,所以想请你帮他回答这些问题。
题解:查询区间内不同数的个数。标准主席树。。
我就直接上板子了。这里记得用c的输入输出否则TLE。讨论区好像set暴力可行。。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn = 30010; 5 const int M = maxn*100; 6 7 int a[maxn]; 8 int T[maxn],lson[M],rson[M]; 9 int c[M]; //保存当前根节点中元素的个数 10 11 int n,m,q,tot; 12 13 //建树 14 int build(int l,int r){ 15 int root = tot++; 16 c[root] = 0; 17 if(l!=r){ 18 int mid = (l+r)>>1; 19 lson[root] = build(l,mid); 20 rson[root] = build(mid+1,r); 21 } 22 return root; 23 } 24 25 int update(int root,int pos,int val){ 26 int newroot = tot++; 27 int tmp = newroot; 28 c[newroot] = c[root] + val; 29 int l = 1,r = n; 30 while(l < r){ 31 int mid = (l+r)>>1; 32 if(pos <= mid){ 33 lson[newroot] = tot++; 34 rson[newroot] = rson[root]; 35 newroot = lson[newroot]; 36 root = lson[root]; 37 r = mid; 38 } 39 else{ 40 rson[newroot] = tot++; 41 lson[newroot] = lson[root]; 42 newroot = rson[newroot]; 43 root = rson[root]; 44 l = mid+1; 45 } 46 c[newroot] = c[root]+val; 47 } 48 return tmp; 49 } 50 51 int query(int root,int pos){ 52 int ret = 0; 53 int l = 1,r = n; 54 while(pos < r){ 55 int mid = (l+r)>>1; 56 if(pos <= mid){ 57 r = mid; 58 root = lson[root]; 59 } 60 else{ 61 ret += c[lson[root]]; 62 root = rson[root]; 63 l = mid+1; 64 } 65 } 66 return ret + c[root]; 67 } 68 69 70 int main(){ 71 scanf("%d%d",&n,&m); 72 //cin>>n>>m; 73 tot = 0; 74 for(int i = 1;i <= n ;i++){ 75 scanf("%d",&a[i]); 76 //cin>>a[i]; 77 } 78 T[n+1] = build(1,n); 79 map<int,int>mp; 80 for(int i = n ;i >= 1;i--){ 81 if(mp.find(a[i]) == mp.end()){ 82 T[i] = update(T[i+1],i,1); 83 } 84 else{ 85 int tmp = update(T[i+1],mp[a[i]],-1); 86 T[i] = update(tmp,i,1); 87 } 88 mp[a[i]] = i; 89 } 90 scanf("%d",&q); 91 //cin>>q; 92 while(q--){ 93 int l,r; 94 scanf("%d%d",&l,&r); 95 //cin>>l>>r; 96 printf("%d\n",query(T[l],r)); 97 //cout<<query(T[l],r)<<endl; 98 } 99 100 return 0; 101 }
5、Array
题意:小红有两个长度为n的排列A和B。每个排列由[1,n]数组成,且里面的数字都是不同的。
现在要找到一个新的序列C,要求这个新序列中任意两个位置(i,j)满足:
如果在A数组中C[i]这个数在C[j]的后面,那么在B数组中需要C[i]这个数在C[j]的前面。
请问C序列的长度最长为多少呢?
题解:这个题最开始想到是逆序B数组,找AB的LCS。二维DP,明显TLE(30%)
然后优化成一维DP,还是不行(40%)交完卷以后康康别人的讨论得到灵感。
我们用一个map来标记A数组中数字的索引。在B数组的输入时直接给当前数字的索引。
转化成寻找最长下降子序列(感叹一下这个操作真的很巧妙。。)
举例:A:12435 B:52341
A: 1 2 4 3 5
mp: 0 1 2 3 4
B: 4 1 3 2 0
此时就是找B中最长下降子序列:4 3 2 0得到长度是4。
这里要用O(nlogn)优化过时间的算法。可以自己写二分,也可以用STL自带。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn = 1e5+10; 5 const int inf = 0xfffffff; 6 int n; 7 int b[maxn]; 8 int dp[maxn]= {0}; 9 10 /* 11 int search(int x){ 12 int l,r,mid; 13 l = 0;r = n; 14 while(l < r){ 15 mid = (l+r)/2; 16 if(b[mid] >= x) r = mid; 17 else l = mid+1; 18 } 19 20 return l; 21 }*/ 22 23 24 int main(){ 25 26 cin>>n; 27 int x; 28 29 map<int,int>mp; 30 31 for(int i = 0; i < n; i++){ 32 cin>>x; 33 mp[x] = i; 34 } 35 for(int i = 0; i < n ;i++){ 36 cin>>x; 37 b[i] = mp[x]; 38 } 39 /* 40 for(int i = 0; i < n ;i++){ 41 cout<<b[i]<<" "; 42 } 43 cout<<endl;*/ 44 45 memset(dp,0,sizeof(dp)); 46 47 int ans = 0; 48 dp[0] = b[0]; 49 50 for(int i = 1 ; i < n ;i++){ 51 if(b[i] < dp[ans]) 52 dp[++ans] = b[i]; 53 else 54 //dp[search(b[i])] = b[i]; 55 dp[lower_bound(dp, dp+n, b[i], greater <int> ()) - dp] = b[i]; 56 } 57 58 59 cout<<ans+1<<endl; 60 61 62 return 0; 63 }
6、魔法排列
题意:众所周知,集合{1 2 3 … N}有N!种不同的排列,假设第i个排列为Pi且Pi,j是该排列的第j个数。将N个点放置在x轴上,第i个点的坐标为xi且所有点的坐标两两不同。对于每个排列(以Pi为例),可以将其视为对上述N个点的一种遍历顺序,即从第Pi,1个点出发,沿直线距离到达第Pi,2个点,再沿直线距离到达第Pi,3个点,以此类推,最后到达第Pi,N个点,将该路线的总长度定义为L(Pi),那么所有N!种路线的总长度之和是多少,即L(P1)+L(P2)+L(P3)+...+L(PN!)的结果是多少?
题解:用STL里next_permutation能过26.67%。当时时间已经不够了。。。
讨论区大佬的题解写的很详细。。但是我还是有点不太会推。。等我下午推了我补一下过程。
先给公式。。
$num_k = [k*(N-k) + k*(N-k)]*(N-1)! = 2*k*(N-k)*(N-1)!$
$D_{total} = \sum ^{N-1}_{k = 1} num_k * d_k$
取模一定要记得每一步最好都要取模,不能一下子一起取,会出现溢出的情况。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int maxn = 1e5+10; 5 const int mod = 1e9+7; 6 7 int num[maxn]; 8 ll ans = 0; 9 int n; 10 11 int main(){ 12 cin>>n; 13 for(int i = 0; i < n ;i++){ 14 cin>>num[i]; 15 } 16 ll fac = 1; //n-1的阶乘 17 for(int i = 1;i < n ;i++) 18 fac = (fac*i + mod)%mod; 19 20 ll dis = 0; //表示d 21 ll a; 22 23 for(ll i = 1; i < n ;i++){ 24 dis = (num[i]-num[i-1]) % mod; 25 a = 2*i*(n-i)%mod; 26 a = (a*dis)%mod; 27 ans = (ans+a)%mod; 28 } 29 30 ans = (ans * fac) % mod; 31 32 cout<<ans<<endl; 33 return 0; 34 }