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的题我只是大致说了一下做题思路,如果你要是不明白为什么这么写,那么你得先去仔细学习一下这几个算法的实现原理以及代码。
如果有什么问题或者还有什么疑问的可以在群里反馈或者私信我 随时欢迎!

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