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

看群友在水这个比赛玩玩,于是也去打了一下重现赛。
事实证明,群友轻松AK,而我只会暴力。
总的难度就是PAT甲的难度吧,数据很不错,恶心的一批。
补题完结

2021RoboCom机器人开发者大赛官方题解

7-1 懂的都懂 (20 分)【简单 / 暴力 + 哈希表】

方法一: 自己的傻叉做法 预处理所有的4个数的平均值。用double 注意精度

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
double a[N],b[N];
map<double,int>mp;
int main(void)
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++) scanf("%lf",&a[i]);
	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 z=k+1;z<n;z++)
				{
					int temp=a[i]+a[j]+a[k]+a[z];
					mp[temp/4.0]++;
				}
			}
		}
	}
	while(m--)
	{
		int t; scanf("%d",&t);
		bool flag=1;
		for(int i=0;i<t;i++) 
		{
			scanf("%lf",&b[i]);
			if(!mp[b[i]]) flag=false;
		}
		if(flag) puts("Yes");
		else puts("No");
	}
	return 0;
}

方法二: 只需处理所有的四个数的和即可,不用求平均

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],b[N];
map<int,int>mp;
int main(void)
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	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 z=k+1;z<n;z++)
				{
					int temp=a[i]+a[j]+a[k]+a[z];
					mp[temp]++;
				}
			}
		}
	}
	while(m--)
	{
		int t; scanf("%d",&t);
		bool flag=1;
		for(int i=0;i<t;i++) 
		{
			scanf("%d",&b[i]);
			if(!mp[b[i]*4]) flag=false;
		}
		if(flag) puts("Yes");
		else puts("No");
	}
	return 0;
}

7-2 芬兰木棋 (25 分)【中 / map 贪心】

大木棋会打倒这个方向上离哲哲最近的 k 个小木棋
读了半天的假题。

1 1 1
2 2 2
3 3 1
步数为3 先打123
#include<bits/stdc++.h>
using namespace std;
typedef long long  LL;
LL ans,n,cnt;
LL gcd(LL a,LL b) {return b?gcd(b,a%b):a;}
map<pair<LL,LL>,vector< pair<LL,LL> > >mp;//斜率映射到对应的距离加分数
int main(void)
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		LL x,y,c; cin>>x>>y>>c;
		ans+=c;
		LL temp=gcd(abs(x),abs(y));
		mp[{x/temp,y/temp}].push_back({x*x+y*y,c}); //注意: 距离必须是x*x+y*y 单独的x是不行的因为还有y轴上的点
	}
    cnt=0;
	for(auto i=mp.begin();i!=mp.end();i++)//枚举所有的方向
	{
		auto ve=i->second;
		sort(ve.begin(),ve.end());//距离排序
		for(int j=0;j<ve.size();j++)
		{
			if(ve[j].second!=1) 
			{
				cnt++;
				if(j-1>=0&&ve[j-1].second==1) cnt++;
			}
		}
		if(ve[ve.size()-1].second==1) cnt++;
	}
	cout<<ans<<" "<<cnt;
	return 0;
}

7-3 打怪升级 (25 分) 【中 / 最短路 记得维护值 和 存路径】

写的可以过,不过有点太卡时间。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,t,x;
int g[N][N],dist[N],st[N];
int startx,maxv=1e9;
int p[N][N];
map<int,int>val,ans;//val存的是该点最大的价值,ans存路径 
void Dijkstra(int x)
{
	memset(dist,0x3f,sizeof dist);
	memset(st,0,sizeof st);
	dist[x]=0;
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++) if(!st[j]&&(t==-1 || dist[j]<dist[t] )) t=j;
		st[t]=1;
		for(int j=1;j<=n;j++)
		{
			if(dist[j]>dist[t]+g[t][j])
			{
				dist[j]=dist[t]+g[t][j];
				val[j]=val[t]+p[t][j];
				ans[j]=t;
			}
			else if(dist[j]==dist[t]+g[t][j])
			{
				if(val[j]<val[t]+p[t][j])
				{
					val[j]=val[t]+p[t][j];
					ans[j]=t;
				}
			}
		}
	}
	int temp=0;
	for(int i=1;i<=n;i++) temp=max(temp,dist[i]);
	if(temp<maxv) maxv=temp,startx=x;
}
int main(void)
{
	memset(g,0x3f,sizeof g);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		g[a][b]=g[b][a]=c;
		p[a][b]=d,p[b][a]=d;
	}
	for(int i=1;i<=n;i++) Dijkstra(i);//每一个点跑一下最短路,找到最短路中最大的值,最小的值
	printf("%d\n",startx);
	val.clear(),ans.clear();
	Dijkstra(startx);
	scanf("%d",&t);
	while(t--)
	{
		int x; scanf("%d",&x);
		int node=x;
		vector<int>path;
		while(node)
		{
			path.push_back(node);
			node=ans[node];
		}
		for(int i=path.size()-1;i>=0;i--) 
		{
			if(i!=path.size()-1) printf("->");
			printf("%d",path[i]);
		}
		printf("\n%d %d\n",dist[x],val[x]);
	}
	return 0;
}

7-4 疫情防控 (30 分)【难度: 难 / 并查集 思维】

很轻松的暴力做法骗22分

#include<bits/stdc++.h> 
using namespace std;
const int N=1e5*3+10;
int h[N],e[N],ne[N],idx;
int st[N],vis[N];
int n,m,t;
int startx,endx;
bool flag;
void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void dfs(int u)
{
	st[u]=1;
	if(u==endx&&!vis[u]) 
	{
		flag=1;
		return;
	}
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!st[j]&&!vis[j]) dfs(j);
	}
}
int main(void)
{
	memset(h,-1,sizeof h);
	cin>>n>>m>>t;
	for(int i=0;i<m;i++)
	{
		int a,b; cin>>a>>b;
		add(a,b),add(b,a);
	}
	while(t--)
	{
		int c,q; cin>>c>>q;
		vis[c]=1;
		int ans=q;
		for(int i=0;i<q;i++)
		{
			memset(st,0,sizeof st);
			flag=0;
			cin>>startx>>endx;
			if(!vis[startx])dfs(startx);
			if(flag) ans--;
		}
		cout<<ans<<endl;
	}
	return 0;
}

正解: 考虑将所有操作离线,逆序处理。即,先删除要删除的所有点,再倒着往里加点,顺便把询问处理掉,用并查集维护即可

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int> PII;
vector<int>ve[N];//存边 
vector<PII>vis[N];
int p[N],s[N],ans[N];
int n,m,t;
int find(int x) 
{
	if(x!=p[x]) p[x]=find(p[x]);
	return p[x];
}
int main(void)
{
	cin>>n>>m>>t;
	for(int i=1;i<=n;i++) p[i]=i;
	for(int i=1;i<=m;i++)
	{
		int a,b; cin>>a>>b;
		ve[a].push_back(b) ;
		ve[b].push_back(a);
	}
	for(int i=1;i<=t;i++)
	{
		int k,a,b; 
		cin>>s[i]>>k;
		p[s[i]]=0;//标记为坏的 
		while(k--)
		{
			cin>>a>>b;
			vis[i].push_back({a,b});
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(p[i])
		{
			for(int j=0;j<ve[i].size();j++)
			{
				if(p[ve[i][j]]) p[find(i)]=find(ve[i][j]);
			}
		}
	}
	for(int i=t;i;i--)
	{
		for(int j=0;j<vis[i].size();j++)//查询
		{
			int fa=find(vis[i][j].first);
			int fb=find(vis[i][j].second);
			if(fa!=fb || fa==0 ) ans[i]++;
		}
		int k=s[i]; p[k]=k;//添加点
		for(int j=0;j<ve[k].size();j++)//并查集处理
		{
			if(p[ve[k][j]]) p[find(k)]=find(ve[k][j]);
		}
	}
	for(int i=1;i<=t;i++) cout<<ans[i]<<endl;
	return 0;
}
  • 25
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值