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 }