ZUA_Coder天團假期歡樂賽總結【全】

苟有恆,何必三更起五更眠;
最無益,只怕一日暴十日寒

寫在前面:這次出題的時候其實很糾結,好久不見也不知道大家水平提高了多少,害怕出的題目難了很少有人做出來,又怕出的題簡單了喪失了比賽的意義。下面我大致分析一下這套題。我們出題的時候我個人計劃的是不能還像剛學完c語言那樣,只要腦子快,for循環全能解出來。所以這次算法題佔大多數。

本次主要考察了 數據結構,DFS,BFS,最短路,並查集,還有稍複雜的模擬。這也是天梯賽可能會考察的幾個方向,希望大家通過這次能發現自己的不足,如果這次你AK了,也希望你別驕傲,畢竟出題人也水平有限,找的題也基本是這些算法的模板題,並沒有去刻意卡時間的複雜度。還是希望大家繼續加油吧!(寫作不易,給個點贊走個關注吧。。。哈哈哈)

感謝17級張競原學長爲這次活動提供的技術支持,
還有18級蘇泉和王浩哲同學和我一起出題、策劃本次活動。

(我也不知道爲啥想寫他們仨的名字,可能是想讓更多人知道咱們學院有這麼三個大佬,哈哈哈)

下面開始進入正文:

問題A:再戰猴子出隊!

在這裏插入圖片描述
約瑟夫環算是老問題了,解法很多種,可以用數組、隊列、鏈表等等來對他進行模擬,有興趣的同學可以去查一下,約瑟夫環其實是有一個狀態轉移不用模擬也能做出來(稱之爲數學解法)。不過下面我提供的做法是用STL裏面的模板————隊列 進行模擬。
大致思路:先讓所有人入隊,然後再不停的出隊入隊出隊入隊,這樣每到第m個就只出不入,慢慢的,所有人就都出隊了,代碼如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt=1;
queue<int>q;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)	q.push(i);
	while(!q.empty())
		if(cnt==m)
		{
			cnt=1;
			cout<<q.front()<<" ";
			q.pop();
		}
		else
		{
			q.push(q.front());
			q.pop();
			cnt++;
		}
}

問題B:0202年了,你還是光棍嗎?

在這裏插入圖片描述
這道題選自PTA團隊天梯賽L1部分,相信有很多同學做的時候會感到熟悉,但是我出這道題的時候爲了防止部分同學直接複製粘貼,刻意加了一個條件,如果不合法就直接輸出Error。
下面說一下大致思路:其實題目上的提示已經說的很清楚了,你可以先找一個大於輸入的數且最接近輸入的數的光棍數。比如說輸入的是31,你找到了一個數111,然後你讓111除以31,餘數*10+1,就這麼一直往下循環,直到餘數爲0,這樣既可以直接把每一位輸出來,最後也能直接獲得1的個數。下面貼張隊的代碼:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define pb push_back
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int main() {
	ll x;
	cin >> x;
	if(x%10==5 || x%2==0) {
		puts("Error");
	} else {
		ll flag=0,tot=1,cnt=0;
		while(1) {
			if(tot/x)
				flag=1;
			if(flag)
				cout << tot/x;
			cnt++;
			ll f=tot%x;
			tot=f*10+1;
			if(!f)
				break;
		}
		cout << " " << cnt;
	}
	return 0;
}

問題C:她到底多長

在這裏插入圖片描述

好了,不要糾結爲什麼要用“她”而不是“他,它”了,因爲我待代碼如初戀好吧。
這道題也是選自PTA團隊天梯賽L2部分,原題目描述爲 最長對稱字串。我說一下大致思路吧:
就是你找到一個數爲中間數,然後用兩個移動的下標分別指向這個中間向量的左邊和右邊,同時向兩邊擴散,相同就長度+2,不同就break。 問題來了,這麼做的話你要判斷它是奇數串還是偶數串,所以我們在這裏加一步處理,讓接收的這個字符串首尾和每兩個字符之間都加上一個字符“#(其實什麼字符都可以),這樣不管用戶輸入的是啥經你處理後都是奇數串,然後就可以進行上面說的思路。代碼在我這篇博客裏L2-008 最長對稱子串 (25分)【20行極短代碼】

問題D:大方的張同學

在這裏插入圖片描述
這道題選自洛谷揹包問題動態規劃,原題目爲”小A點菜“。有經驗的同學應該一眼就看出來是揹包了,不過我出這道題的本意並不是要讓大家用動歸來寫,這道題解法有很多種,可以DFS暴力搜索,也可以DP,當然如果你思路極其清晰,用for循環解出來也不是不可能,哈哈哈! 下面提供一種我的解法,用到的DFS,在此對思路不做任何解釋了,如果你會DFS原理的話,下面這個代碼你很快就能看明白,如果沒看過的話,我三言兩語確實解釋不清。代碼如下:

#include<bits/stdc++.h>
using namespace std;
int a[101],n,m,sum,b[101];
void dfs(int k,int x)
{
	if(k>m)	return ;
	if(k==m)	{sum++;	return ;}
	for(int i=x+1;i<=n;i++)
		if(!b[i])
		{
			b[i]=1;
			dfs(k+a[i],i);
			b[i]=0;
		}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)	cin>>a[i];
	dfs(0,0);
	cout<<sum;
}

問題E:傳家寶

在這裏插入圖片描述
這道題選自PTA團隊天梯賽L2部分,原題目爲”功夫傳人“。我個人解法是 並查集+DFS,DFS你可以不用只要你能算出來它是第幾代,不過並查集是肯定要用的吧。具體大家看我這篇博客,裏面思路講解很清楚,代碼也很簡單,好吧其中有一種解法我是粘過來的L2-020 功夫傳人 (25分)(雙解法 完整思路+極短代碼)

問題F:排隊

在這裏插入圖片描述
這道題選自洛谷線性數據結構部分,原題目爲”隊列安排“,其實本意是想考察大家是否會建樹了,不過這道題由於數據太過簡單,用結構體數組完全可以模擬。詳細思路和代碼在我這篇博客裏:隊列安排【洛谷】

問題G:二叉樹遍歷的相互轉化

在這裏插入圖片描述
這道題算是一道二叉樹遍歷的模板題了,毫無任何技術可言。如果不會三種遍歷相互轉化的可以參考一下我的這篇文章,實在不懂就吧模板記住,也沒幾行,記住幾行代碼以後很受益的前序,中序,後序三種遍歷的相互轉化(思路+遞歸實現代碼)【全】
然後這道題的代碼如下:

#include<bits/stdc++.h>
using namespace std;
string in,post;
void pre(int root,int start,int end)
{
	if(start>end)	return ;
	int i=start;
	while(i<end&&in[i]!=post[root])	i++;
	cout<<post[root];
	pre(root-1-end+i,start,i-1);
	pre(root-1,i+1,end);
}
int main()
{
	cin>>in>>post;
	pre(post.size()-1,0,post.size()-1);
}

問題H:汝曾聽聞——象棋?

在這裏插入圖片描述
這道題選自洛谷的寬度優先搜索,原題目爲“馬的遍歷” 但是這裏我簡化了很多,測試數據很小,如果你今天比賽的時候AC了你可以把你的代碼粘到洛谷上提交一下試試,說不定就過不去。。哈哈哈。
這道題也是一道基礎的BFS模板題,可能唯一不同的就是不想大部分BFS只有上下左右四個方向,不過”馬走日“嘛,也比較好表示。不多說了,代碼如下:

#include<bits/stdc++.h>
using namespace std;
struct xy
{
	int x,y;
}node,Top;
const int dx[4]={1,-1,2,-2};
const int dy[4]={1,-1,2,-2};
int a[401][401];
bool b[401][401];
int n,m;
void bfs(int x,int y,int step)
{
	a[x][y]=step;
	b[x][y]=false;
	queue<xy>Q;
	node.x=x;
	node.y=y;
	Q.push(node);
	while(!Q.empty())
	{
		Top=Q.front();
		Q.pop();
		for(int i=0;i<4;i++)
			for(int j=0;j<4;j++)
				if(abs(dx[i])!=abs(dy[j])) 
				{
					int NewX=Top.x+dx[i];
					int NewY=Top.y+dy[j];
					if(NewX<1||NewX>n||NewY<1||NewY>m)	continue;
					if(b[NewX][NewY])
					{
						node.x=NewX;
						node.y=NewY;
						Q.push(node);
						b[NewX][NewY]=false;
						a[NewX][NewY]=a[Top.x][Top.y]+1;
					}
				}
	}
}
int main()
{
	memset(b,true,sizeof(b));
	memset(a,-1,sizeof(a));
	int x,y;
	cin>>n>>m>>x>>y;
	bfs(x,y,0);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			printf("%-5d",a[i][j]);
		cout<<endl;
	}
	return 0;
}

問題I:最短路

在這裏插入圖片描述
這道題是泉哥出的,本意是想考察堆優化版的dijkstra,後來害怕太難沒人做出來,刪掉了最大的卡時間的數據,簡化後這道題用樸素dijkstra算法完全可以做,也算是一道板子題了,只不過和平常的dijkstra不同的是這道題求的是乘積之和最短的路。加號改成乘號就行了,不過有一點需要注意的是,既然是乘積,雖然一個數可能在int範圍內,但是如果是兩個很大的int,乘積可能就會超過int,所以要用到longlong記錄。如果不會最短路問題的可以看我這篇博客五種最短路算思路及其代碼實現【全】
然後這道題我的代碼如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010,inf=0x3f3f;
int n,m;
long long dist[maxn],e[maxn][maxn],ans;
bool st[maxn];
long long dijkstra()
{
	for(int i=1;i<n;i++)
	{
		int u=-1;
		for(int j=1;j<=n;j++)
			if(!st[j]&&(u==-1||dist[j]<dist[u]))	u=j;
		st[u]=true;
		for(int j=1;j<=n;j++)	
			if(!st[j])	dist[j]=min(dist[j]%9987,dist[u]*e[u][j]%9987);
	}
	return dist[n];
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i==j)	e[i][j]=1;
			else	e[i][j]=inf;
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		e[a][b]=c;
	}
	for(int i=1;i<=n;i++)	dist[i]=e[1][i];
	cout<<dijkstra();
}

問題J:我是特工

這道題選自編程隊寒假作業題集L2部分的一道題,咳,這道題算是一道比較麻煩的DFS,如果你思路清晰的話做起來可能也就費點時間,爲什麼選這道題呢?學長選的。。。(小聲bb,因爲這道題我也不會。。)
下面直接貼張學長的代碼,看完下面的代碼在看上面我的代碼你就能發現大佬和菜雞的區別了(反正這麼多宏我是不習慣用)

#include<bits/stdc++.h>
#define pb push_back
#define SZ(x) (ll)x.size()
#define rep(i,a,b) for(ll i=(a);i<=(b);++i)
#define per(i,a,b) for(ll i=(a);i>=(b);--i)
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int maxn=1e4+5,inf=0x3f3f3f3f;
struct Node{
	ll x,y;
}g[maxn];
ll n,d,f=0,vis[maxn];
double dist(ll x,ll y){
	return sqrt((g[x].x-g[y].x)*(g[x].x-g[y].x)+(g[x].y-g[y].y)*(g[x].y-g[y].y));
}
void dfs(ll x){
	if(f || !(abs(g[x].x+d)<50 && abs(g[x].y+d)<50)){
		f=1;
		return ;
	}
	vis[x]=1;
	rep(i,1,n){
		double dis=dist(x,i);
		if(!vis[i] && dis<=d)
			dfs(i);
	}
}
int main(){
	cin >> n >> d;
	rep(i,1,n){
		cin >> g[i].x >> g[i].y;
	}
	rep(i,1,n){
		double dis=dist(i,0);
		if(dis<=d+7.5){
			mem(vis,0);
			dfs(i);
		}
		if(f){
			puts("Yes");
			return 0;
		}
	}
	puts("No");
	return 0;
}

這就是本次比賽的全部內容了,後面涉及到DFS,BFS,Dijkstra的題我只是大致說了一下做題思路,如果你要是不明白爲什麼這麼寫,那麼你得先去仔細學習一下這幾個算法的實現原理以及代碼。
如果有什麼問題或者還有什麼疑問的可以在羣裏反饋或者私信我 隨時歡迎!

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