【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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

 

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 }
View Code

 

posted @ 2020-03-23 13:34  甜酒果。  阅读(884)  评论(2编辑  收藏  举报