2021 RoboCom 世界机器人开发者大赛-本科组(初赛)已完结

目录

7-1 懂得都懂

 具体代码实现:

7-2 芬兰木棋

具体实现代码:

7-3 打怪升级

​编辑​编辑

具体实现代码:

1.采用dijkstra实现

2. 采用floyed实现 

7-4 疫情防控

具体代码实现:


题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)

7-1 懂得都懂

    输入样例:

5 3
4 8 12 20 40
3 11 16 19
3 12 16 19
10 11 11 11 11 11 11 11 11 11 11

输出样例:

Yes
No
Yes

 具体代码实现:

//思路:每个数据都可以由原图任意4个不同数据的平均值计算而来
//1. 如果我们用一个数组来存储4个数据的和->数组大小为50*49*48*47(int足够)
//但是在查询的时候,需要每次进行一个遍历(高达200*200) ->不使用 
//2. 原图四个值的和最大为 255*4,如果该值存在则标记为1,
//在查询值的时候只需查询该值是否为1即可。 
#include<bits/stdc++.h>
using namespace std;
int f[1100],a[60],news[210]; //记录原图四个数据和、原图特征数据、新图特征数据 
int m;
bool judge(){
	for(int i=0;i<m;i++){
		int t=news[i]*4;
		if(!f[t])
			return false; 
	}
	return true;
}  
int main(void){
	int n,k; //原图的特征数据个数、新图的张数
    cin>>n>>k;
	for(int i=0;i<n;i++){ 
		cin>>a[i];   //a存储原图特征数据 
	}
	//1.记录原图任意四个数据的和(不用平均导致小数)
	for(int i=0;i<n;i++){
		for(int j=i+1;j<n;j++){
			for(int k=j+1;k<n;k++){
				for(int l=k+1;l<n;l++){
					int t=a[i]+a[j]+a[k]+a[l];
					f[t]=1;
				}
			}
		}
	}
	for(int i=0;i<k;i++){
		cin>>m;
		for(int j=0;j<m;j++){
			cin>>news[j];
		}
		if(i!=0)
			cout<<endl;
		if(judge())
			cout<<"Yes";
		else
			cout<<"No";	 
	} 
	 
} 

7-2 芬兰木棋

输入样例:

11
1 2 2
2 4 3
3 6 4
-1 2 2
-2 4 3
-3 6 4
-1 -2 1
-2 -4 1
-3 -6 1
-4 -8 2
2 -1 999

输出样例:

1022 9

具体实现代码:

//思路:贪心
//1. 怎么获得的分数最多,次数最少 
//木棋分数>1,+木棋上的分数(一次一根) 
//木棋分数<=1,判断连续<=1的个数,一次击倒
//显而易见:最多分为木棋上的值的和 
//2. 怎么确认方向
//1)最开始我想的是k,但是在k相等的条件下有两个方向—>不使用
//2)找到x,y两个位置的最大公约数(正),除以公约数之后相同则表示同一方向(因为正负不会发生改变)
//3. 怎么在同一方向下距离由小到大排序
//存储距离--》x平方+y平方,还需要存储木棋值
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<pair<ll,ll>,vector<pair<ll,ll> > >mp;
int main(void){
	int n;
	ll x,y,p;
	cin>>n;
	ll smax=0,nmax=0;  //最大得分、最少次数 
	for(int i=0;i<n;i++){
		cin>>x>>y>>p;
		int t=abs(__gcd(x,y));  //得到最大公约数
		mp[{x/t,y/t}].push_back({x*x+y*y,p}); 
		smax+=p; 
	}
	for(auto it:mp){  //使用for循环,则访问用. 
		vector<pair<ll,ll> >t=it.second;
		sort(t.begin(),t.end());
		for(int i=0;i<t.size();i++){
			if(t[i].second!=1)
				nmax++;
			else{
				if(t[i+1].second!=1||i+1==t.size())
					nmax++;
			}
		}
	}
	cout<<smax<<" "<<nmax;
	return 0;
}

tips:

遍历 map:可以使用迭代器或者范围 for 循环。

  • 迭代器:for (auto it = map.begin(); it != map.end(); ++it)

  • 范围 for 循环:for (const auto& pair : map)

访问 map 的元素

  • 使用迭代器:it->first, it->second

  • 使用范围 for 循环:pair.first, pair.second

访问 pair 的元素pair.first, pair.second

7-3 打怪升级

 

输入样例:

6 12
1 2 10 5
2 3 16 20
3 1 4 2
2 4 20 22
4 5 2 2
5 3 12 6
4 6 8 5
6 5 10 5
6 1 20 25
1 5 8 5
2 5 2 1
2 6 8 5
4
2 3 6 5

  输出样例: 

5
5->2
2 1
5->1->3
12 7
5->4->6
10 7
5
0 0

具体实现代码:

1.采用dijkstra实现

//思路:采用迪杰斯特拉算法
//1.首先进行n次迪杰斯特拉算法得到空降堡垒编号,也就是需要我们存储每一个的最大值
//2. 求path,使用while即可
#include<bits/stdc++.h>
#define maxInt 0x3f3f3f
#define N  1010
using namespace std;
int w[N][N],v[N][N];  //能量、价值
int path[N],d[N],f[N];  //前驱点、最小能量,最大价值 
bool vis[N];  //是否被访问过 
int n,m,k; 
vector<int> p; //存储具体路径 
int  dijkstra(int s){
	memset(d,maxInt,sizeof(d));
	memset(f,0,sizeof(f));
	memset(path,-1,sizeof(path));
	memset(vis,0,sizeof(vis));
	d[s]=0;
	f[s]=0;
	for(int k=1;k<=n;k++){   //n次选点 
		//1. 寻找未被访问的点距离最小的 
		int t=-1;  //最小能量下标
		for(int i=1;i<=n;i++){
			if(!vis[i]&&(t==-1||d[t]>d[i]))
				t=i;
		}
		vis[t]=true;
		//2.更新
		for(int i=1;i<=n;i++){
			if(d[i]>d[t]+w[t][i]){
				d[i]=d[t]+w[t][i];
				f[i]=f[t]+v[t][i]; 
				path[i]=t; 
			}else if(d[i]==d[t]+w[t][i]){
				//选择价值最大的 
				if(f[i]<f[t]+v[t][i]){
					f[i]=f[t]+v[t][i];
					path[i]=t;
				}
			}
		} 
	}
	//3. 存储最大d[]值
	int ans=0;
	for(int i=1;i<=n;i++){
		if(ans<d[i])
			ans=d[i];
	} 
	return ans;
}
void findPath(int s,int d){
	p.clear(); 
	while(d!=s){  //倒叙存储,并且不包含源点 
		p.push_back(d);
		d=path[d];
	}
	p.push_back(s);
	reverse(p.begin(),p.end()); 
} 
int main(void){
	cin>>n>>m;
	//初始化
	memset(w,maxInt,sizeof(w));
	memset(v,maxInt,sizeof(v));
	memset(v,maxInt,sizeof(f));
	memset(d,maxInt,sizeof(d)); 
	memset(path,-1,sizeof(path)); 
	for(int i=0;i<m;i++){
		int b1,b2,c1,c2;
		cin>>b1>>b2>>c1>>c2;
		w[b1][b2]=w[b2][b1]=c1;
		v[b1][b2]=v[b2][b1]=c2;
	}
	for(int i=1;i<n;i++){
		v[i][i]=w[i][i]=0; //自己到自己等于0 
	}
	int x=1;
	int ans=dijkstra(1);  
	for(int i=2;i<=n;i++){
		if(ans>dijkstra(i)){
			ans=dijkstra(i);
			x=i;	
		}	
	}
	cout<<x<<endl;  //得到最佳空降位置
	dijkstra(x);
	cin>>k;
	for(int i=0;i<k;i++){
		int t;
		cin>>t;
		findPath(x,t);
		for(int j=0;j<p.size();j++){
			if(j>0)
				cout<<"->";
			cout<<p[j];
		}
		cout<<endl;
		cout<<d[t]<<" "<<f[t]<<endl;
	} 
	return 0;
}
 

2. 采用floyed实现 

//思路:只需要一次floyed即可
//1. 求合适空降点:对每一行w,求最大的值最小的行
//2.求路径:path递归求解
#include<bits/stdc++.h>
#define maxInt 0x3f3f3f3f
#define N 1010
using namespace std;
int n,m,k;
int w[N][N],v[N][N],path[N][N];  //能量、价值 
void floyed(){
	for(int k=1;k<=n;k++){  //k为中转点 
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(w[i][j]>w[i][k]+w[k][j]){
					w[i][j]=w[i][k]+w[k][j];
					v[i][j]=v[i][k]+v[k][j];
					path[i][j]=k;
				}else if(w[i][j]==w[i][k]+w[k][j]){
					//找总价值最高的
					if(v[i][j]<v[i][k]+v[k][j]){
						v[i][j]=v[i][k]+v[k][j];
						path[i][j]=k;
					} 
				}
			} 
		} 
	} 
}
//寻找具体路径 
void findPath(int st,int end){
	if(st==end){
		return;
	}
	if(path[st][end]==(-1)){  //不用判断w[][],因为路径肯定存在 
		cout<<"->"<<end; 
	}else{
		int mid=path[st][end];
		findPath(st,mid);
		findPath(mid,end);
	}
}
int main(void){
	cin>>n>>m;
	//初始化
	memset(w,maxInt,sizeof(w));
	memset(v,0,sizeof(v));
	memset(path,-1,sizeof(path)); 
	for(int i=1;i<N;i++){
		w[i][i]=0;
		v[i][i]=0;
	}
	for(int i=0;i<m;i++){
		int b1,b2,c1,c2;
		cin>>b1>>b2>>c1>>c2;
		w[b1][b2]=w[b2][b1]=c1;
		v[b1][b2]=v[b2][b1]=c2;
	}
	floyed();
	//找空降位置:数值最大的最小行 
	int x=0; //目标行(空降位置) 
	int ans=maxInt; //目标行的最大值 
	for(int i=1;i<=n;i++){ //n行进行计算 
		int t=0; 
		for(int j=1;j<=n;j++){ //找到第i行的w的最大值 
			if(w[i][j]>t)
				t=w[i][j];
		}
		if(ans>t){
			x=i;
			ans=t;
		}
	}
	cout<<x<<endl;
	cin>>k;
	for(int i=0;i<k;i++){
		int d;
		cin>>d;
		cout<<x;
		findPath(x,d);
		cout<<endl;
		cout<<w[x][d]<<" "<<v[x][d]<<endl;
	}
	return 0;
} 

7-4 疫情防控

输入样例:

5 5 3
1 2
1 3
1 5
2 5
3 4
4 3
1 3
1 4
2 3
5 3
3 4
2 3
3 5
1 3
2 3
2 5
3 4

输出样例:

1
2
3

具体代码实现:

//题意:n个顶点m条边的无向图,每天都有一个点不可用,问是否可达
//->首先我想到的是每天遍历将其长度设为无穷,然后dfs()
//->每一次判断都要dfs+遍历vis数组;然后去点,时间复杂度太高
//于是使用并查集->加点
//思路:逆序每次加点,故需要存储每次的值
#include<bits/stdc++.h>
using namespace std;
const int N=50010;
vector<int> f[N]; //先存储结构,因为需要逆序 
int fa[N];   //存储父节点 
bool vis[N];   //true表示无法访问 
int n,m,d; //机场数、航线数、新增防控地区的天数 
int dd[N];  //表示第i天变成防空地区的编号
int ans[N];  //存储每天的结果 
map<int,vector<pair<int,int> > > mp;  //存储每天要访问的航线 
void init(){
	for(int i=1;i<=n;i++)
		fa[i]=i;
}
int find(int i){
//	if(fa[i]==i)
//		return i;
//	else{
//		fa[i]=find(fa[i]);
//		return fa[i];
//	}
	if(i!=fa[i])
		fa[i]=find(fa[i]);
	return fa[i];
}
void join(int i,int j){
	int i_fa=find(i);
	int j_fa=find(j);
	if(i_fa!=j_fa)
		fa[i_fa]=j_fa;
}
int main(void){
	cin>>n>>m>>d;
	for(int i=0;i<m;i++){
		int a,b;
		cin>>a>>b;
		f[a].push_back(b);
		f[b].push_back(a);
	}
	for(int i=1;i<=d;i++){  //d块输入 
		int c,q;
		cin>>c>>q;
		dd[i]=c;
		vis[c]=true; //true表示成为防控地区 
		for(int j=1;j<=q;j++){  //q段行程 
			int x,y;
			cin>>x>>y;
			mp[i].push_back({x,y}); 
		} 
	}
	//逆序使用并查集
	//1.初始化父节点 
	init();
	//2. 构建最后一天的连通图 
	for(int i=1;i<=n;i++){
		for(auto x:f[i]){
			if(!vis[x]&&!vis[i]){ //两点均不是疫情点 
				join(i,x); 
			}
		}
	}
	//3.逆向处理每天的起飞请求
	for(int i=d;i>0;i--){
		int res=0;
		vector<pair<int,int> >v=mp[i];
		for(int j=0;j<v.size();j++){
			int x=v[j].first;
			int y=v[j].second;
			if(find(x)!=find(y))
				res++;
		}
		ans[i]=res; 
		//4. 恢复前一天的疫情点
		int t=dd[i];
		vis[t]=false;  
		for(auto x:f[t]){
			if(!vis[x]){
				join(t,x);
			}
		} 
	} 
	for(int i=1;i<=d;i++){
		if(i!=1) cout<<endl;
		cout<<ans[i];
	}
	return 0;
} 

代码自己写的,如果有啥错误欢迎指正!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值