快手2020校園招聘秋招筆試--算法C試卷 練習 解題報告 Apare_xzc

快手2020校園招聘秋招筆試–算法C試卷 解題報告 Apare_xzc

2020/4/10


網頁鏈接:牛客鏈接


題型分佈:

        選擇題(2分/道*20道)
        編程題(15分/道*4道)


選擇題中的知識點學習回顧:

在這裏插入圖片描述

線性迴歸中的殘差服從均值(期望)爲0的高斯分佈(正態分佈)。


在這裏插入圖片描述

一次不定方程解的個數:m個盒子放入n個小球
盒子非空:插板法:C(n-1,m-1)
盒子可空:先轉化爲等價非空:C(m+n-1,m-1)


在這裏插入圖片描述
在這裏插入圖片描述

直線切分平面:n*(n+1)/2+1
平面分割空間:(n^3+5n+6)/6


在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述


編程題有4道

21. 運動會

在這裏插入圖片描述

輸入例子1

3
3 10
1 5
4 6

輸出例子1

1

分析:

        加油的時長爲(ed-st)/2+1, 每個節目我們可以計算出最少加油時長和最晚開始時間。
        按照最晚開始時間排序,然後貪心檢查

代碼:

#include <bits/stdc++.h>
using namespace std;
struct Node{
	int st,ed,x,t; //最遲開始時間
	void getx() {
		t = (ed-st)/2+1;
		x = t+st;
	} 
	bool operator < (const Node& rhs) const {
		return x < rhs.x;
	}
}node[20]; 
int main(void) {
	int n;
	cin>>n;
	for(int i=0;i<n;++i)
		scanf("%d%d",&node[i].st,&node[i].ed),node[i].getx();
	sort(node,node+n);
	int p = node[0].st+node[0].t;
	bool ok = true;
	for(int i=1;i<n;++i) {
		if(p>node[i].x) {
			ok = false;break;
		}
		p = p+node[i].t;
	}
	if(ok) puts("1");
	else puts("-1");
	return 0;
} 

在這裏插入圖片描述


22. 小遊戲

        有位老鐵設計了一個跳格子游戲,遊戲有N個格子順序排成一行,編號從1到N,每個格子有點數Qi,有標記Li(標記的範圍是1-M),每次跳格子,要選擇一個格子a,以任意正偶數距離x跳到格子b,如果格子b在遊戲區域內,且La=Lb,則稱爲一次合法跳躍,獲得的分數是(a + b) * (Qa + Qb)。
在繼續設計遊戲玩法時,這位老鐵糾結了很久,於是他決定放棄……但是他想知道所有合法跳躍總共能獲得多少分。

數據範圍:

    這題不給數據範圍,交上去RE的RE, TLE的TLE, 試了幾次才試出來…
n<=1E5
m<=1E4

分析:

        每次只能跳到相距偶數個的,Lb相同的格子。開始不知道數據範圍,寫了一發n^2的暴力,結果T了50%的數據。
        我們把可以互相轉移的格子分到一組裏,這樣的話,在組內,不同的格子之間兩兩都對分數有貢獻。我們分組的時候奇數格子和偶數格子要分開。
        如果x和y可以相互到達,那麼(x,y)這對位置對於分數的貢獻就是(x+y)*(Qx+Qy) = x*Qx + y*Qx + y*Qy + x*Qy。我們可以計算一下Qx對於答案的貢獻。如果組內有m個元素,那麼x就要和m-1個位置相互可達。m-1個位置每個貢獻都有xQx, 每個y都有yQx。所以Qx的貢獻就爲x * Qx * (m-1) + Qx * (Qy1 + Qy2 + ... + Qym-1) , 我們可以維護組內Q值的和sum。這樣的話Qx的貢獻就是x * Qx + Qx * (sum - Qx)
        我們可以開兩個數組分別記錄一下每個標誌位L的組元素的個數與Q的和,然後計算即可。

代碼:

#include <bits/stdc++.h>
using namespace std;
const int N = 100000+10; 
int Q[N];
int L[N];
int cnt1[10000+10],cnt2[10000+10];
long long sum1[10000+10],sum2[10000+10];
int main(void) {
	int n,m;
	cin>>n>>m;
	assert(m<=10000);
	for(int i=1;i<=n;++i)
		scanf("%d",Q+i);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",L+i);
		if(i&1) cnt1[L[i]]++,sum1[L[i]]+=i;
		else cnt2[L[i]]++,sum2[L[i]]+=i;
	}
	long long ans = 0;
	for(int i=1;i<=n;++i) {
		if(i&1) {
			int m = cnt1[L[i]];
			if(m<=1) continue; //沒人和它配對
			ans = (ans+1ll*i*Q[i]*(m-1)+1ll*Q[i]*(sum1[L[i]]-i))%10007; 
		} else {
			int m = cnt2[L[i]];
			if(m<=1) continue;
			ans = (ans+1ll*i*Q[i]*(m-1)+1ll*Q[i]*(sum2[L[i]]-i))%10007;
		}
	}
	cout<<ans<<endl;
	return 0;
} 

在這裏插入圖片描述


23. 丟手絹

在這裏插入圖片描述

分析:

        說白了就是一個有向圖,每個節點只有一個出度。找出這個圖中長度最小的環,輸出這個長度。(題目說保證有答案,就是保證有環)
        注意,這個圖可能是不連通的,可能有很多子圖,所以我們要從每個點出發都dfs一次才能確保能找到最優答案。
        這個題每個結點出度爲1,我們甚至不用dfs,直接循環就好了,但是dfs更好寫一點,代碼更簡潔。我們可以從每個點出發dfs,沿着箭頭走。然後標記每個結點在這一趟中出現的次序。如果在這一趟中,某個結點出現了兩次,那麼就說明沿着環走到了之前走過的結點,那麼環的大小就爲這個結點先後兩次的次序只差,我們就結束這趟dfs, 因爲再搜下去也是一樣的路線。
        我們要注意,前幾趟dfs中走過的結點,在後面是不需要走的,因爲之前走過的環,從外部進入不會得到更小的環。所以,我們每個結點走一次即可。複雜度O(n)。

代碼:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int to[N],vis[N],cntt[N];
int ans;
void dfs(int x,int id,int cnt) {//當前結點,在這一輪中的標號,輪數 
	if(vis[x]) {
		if(cntt[x]<cnt) return;
		ans = min(ans,id-vis[x]);
		return;
	}
	vis[x] = id;
	cntt[x] = cnt;
	dfs(to[x],id+1,cnt);
} 
int main(void) {
	ans = 1e7;
	int n;cin>>n;
	for(int i=1;i<=n;++i)
		scanf("%d",to+i);
	for(int i=1;i<=n;++i) {
		if(!vis[i]) dfs(i,1,i);
	}
	cout<<ans<<endl;
	return 0;
} 

在這裏插入圖片描述


24. 有趣的最大池化

在這裏插入圖片描述

樣例輸入1

5
31 24 21 14 22
1

樣例輸出1

31 24 21 14 22

樣例輸入2

5
18 14 31 1 26
2

樣例輸出2

18 31 31 26

樣例輸入3

16
61 53 2 13 51 30 48 44 58 46 36 8 2 8 34 10
7

樣例輸出3

61 53 58 58 58 58 58 58 58 46

分析:

        經典單調隊列問題,解決滑動窗口的最大值。我們可以維護一個單調的隊列,隊列從隊首到隊尾(從左到右)存儲的元素單調遞減。隊首元素即爲該區間的最大元素。
        我們從1到n遍歷原數列中的數,到a[i],我們先去尾, 如果隊尾(最右邊)的元素比a[i]小的話,它對後面所有長度爲len的區間的最大值都是沒有意義的。去尾之後我們將a[i]從隊尾加入到隊列之中。然後我們刪頭,如果隊首代表的元素的位置比隊尾的還小len-1, 那麼說明已經在窗口左區間的左邊了,沒有意義,我們要去掉這個沒有意義的元素。然後此時的隊首就是以i爲區間右端點,長度爲len的區間(窗口)中最大的元素了。
        隊列中只需要存數組元素的下表即可。手動模擬隊列比STL::queue要快一點。

代碼:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5; 
int a[N],S[N];//單調隊列,記錄id,隊尾維護最大值, 
int main(void) {
	int n,len;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d",a+i);
	scanf("%d",&len);
	int pa = 1,pb = 0;
	for(int i=1;i<=n;++i) {
		while(pb>=pa&&a[S[pb]]<=a[i]) --pb;//
		S[++pb] = i;
		while(i-S[pa]+1>len) ++pa;
		if(i>=len) printf("%d ",a[S[pa]]);
	}
	return 0;
} 

在這裏插入圖片描述


2020/4/10 23:28
xzc


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章