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

题目不赘述,在pta教育超市中可以买到。https://pintia.cn/problem-sets/1446838676759703552/exam/problems/1446838732288094208

一.懂得都懂

用深度优先遍历先预处理,之后按照要求找答案即可。

#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[60];
map<int,int> mp;

void dfs(int u,int num,int sum){
	if(num==4){
		mp[sum]=1;
		return;
	} 
    if(u==n+1)return;
    //2.
	dfs(u+1,num,sum);
	//1.
	dfs(u+1,num+1,sum+a[u]);
	
} 


int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    dfs(1,0,0);
    
	while(k--){
		int m;
		cin>>m;
        bool flag=true;
		for(int i=1;i<=m;i++){
            int temp;
			cin>>temp;
			if(!mp[temp*4]){
				flag=false;
				//break;
			}
		}
		if(flag)puts("Yes");
		else puts("No");
		
	} 
    return 0;
}

二.芬兰木棋

使用斜率存储每条直线上的点,按照题目要求遍历每一条直线上的点。(如果难以理解,可以分四个象限使用斜率存储射线)

#include<bits/stdc++.h>
using namespace std;
struct node{
	int x,y,w;
};
unordered_map<double,vector<node> > mp;

bool cmp(node a,node b){
	if(a.x!=b.x)
	return a.x<b.x;
	return a.y<b.y;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	
	int n;
	cin>>n;
	int sum=0;
    while(n--){
    	int x,y,z;
    	cin>>x>>y>>z;
    	sum+=z;//无论是全1(按照个数)  还是按照分数   
    	mp[1.0*y/x].push_back({x,y,z});//需要double类型精确
	}	
    int num=0; 
	for(auto i:mp){//每个方向
	    auto v=i.second;
		sort(v.begin(),v.end(),cmp); 

		for(int j=0;j<v.size();j++){
		    if(j==0||v[j].w!=1||v[j-1].w!=1)
		    num++;//1相连可以算成一次投掷,其他都要单次投掷 
		}
	}
	
	cout<<sum<<' '<<num<<endl;
	
	return 0;
}

三.打怪升级

这道题是最难的一题,题目很难理解,考察floyed多源找最短路确定s,以及dijkstra单源最短路处理双权值问题。本题用堆优化dijktra比较方便,可以省去一部分自己写的代码,但是需要注意greater小根堆需要重载>号,具体原因:https://www.cnblogs.com/jyssh/p/16770809.html

#include<bits/stdc++.h>
using namespace std;
//要求1是多源最短路floyed求出s 
//要求2是用dijkstra求s单源的两个判断标准(用堆优化不用写两种判断) 
//要求1用邻接矩阵,要求2用邻接表(用结构体+vector也能实现) 

typedef long long ll;
typedef pair<int,int> pii;

const int N=1010;
const int inf=1e9+10;

//边为双向边 
#define x first
#define y second

struct node{
	int v,w1,w2;
	bool operator>(const node &x)const{//本在前,x在后 。    小根堆要定义大于号 (大就调整,即内一定不好) 
	   if(w1!=x.w1)return  w1>x.w1;
	   return w2<x.w2; 
	} 
};

vector<node> edge[N];//dijkstra用到邻接表 


int dist1[N],dist2[N];//dijkstra用到 
int pre[N];
bool st[N]; 
int n,m;

void print(int x){
	if(pre[x]==-1)return ;
	print(pre[x]);
	cout<<"->"<<x;
}

void dijkstra(int s){
	//1.初始化 
   priority_queue<node,vector<node>,greater<node>> heap;//找最小(优)
   for(int i = 1; i <= n; i++)dist1[i] = inf,dist2[i]=0, pre[i] = -1;
   dist1[s]=0,dist2[s]=0;
   heap.push({s,dist1[s],dist2[s]});//把s包入S集合中
    
   while(heap.size()){
   	//2.找优 
   	int u=heap.top().v;heap.pop();
   	if(st[u])continue;
   	//3.标记取出 
   	st[u]=true;
   	//4.更新 
   	for(auto ite:edge[u]){
   		 int v=ite.v;
   		 if(dist1[v]>dist1[u]+ite.w1){
   		 	  dist1[v]=dist1[u]+ite.w1;
   		 	  dist2[v]=dist2[u]+ite.w2;
   		 	  pre[v]=u;//最优的推出者
			  heap.push({v,dist1[v],dist2[v]}); 
			}
   		 else if(dist1[v]==dist1[u]+ite.w1&&dist2[v]<dist2[u]+ite.w2){
   		 	  dist2[v]=dist2[u]+ite.w2;
   		 	  pre[v]=u;//最优的推出者
			  heap.push({v,dist1[v],dist2[v]}); 
			}
	   }
   }
   return; 
}


int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	
	int e[N][N];//floyed一个要求用到 
	memset(e,0x3f,sizeof(e));
	
	cin>>n>>m;
    for(int i=1;i<=m;i++){
    	int u,v,w1,w2;
    	cin>>u>>v>>w1>>w2;
    	e[u][v]=e[v][u]=w1;
    	//双向
    	edge[v].push_back({u,w1,w2});
    	edge[u].push_back({v,w1,w2}); 
	}
    
    //floyed解决要求1 
    //e在上方已经初始化完成 
    
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				e[i][j]=min(e[i][j],e[i][k]+e[k][j]); 
			}
		}
	} 
	
	int s=-1,mx=inf;
	for(int i=1;i<=n;i++){
		int temp=0;
		for(int j=1;j<=n;j++)temp=max(temp,e[i][j]);
		if(temp<mx){
			mx=temp;
			s=i;
		}
	}
	
	//dijkstra解决要求2(有两个条件) 
	dijkstra(s);
	
	cout<<s<<endl;
	
	int T;cin>>T;
	while(T--){
	  int x;
	  cin>>x;
	  cout<<s;
	  print(x);//用递归函数(堆栈)实现倒序转化成正序输出	
	  cout<<endl;
	  cout<<dist1[x]<<' '<<dist2[x]<<endl;		
	}
	return 0;
}

四.疫情防控(22分/33分)

图像的连通性问题需要考虑并查集,然而并查集比较方便不断扩大插入,对于删除却不大擅长,并且实现的时间复杂度及其大,于是我们采用离线方法,先把所有输入存起来,再从最后一天的并查集基础上,不断加入新复活的点。

本题只能得到22分,看了下别人的代码,答题思路都一样,可能是因为邻接表和多个表的存储采用了vector存储,不如数组存储的访问时间快。但是vector存储最大优点是方便简单,如果用数组存储邻接表比较难以理解,详见https://www.acwing.com/activity/content/11/

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define x first
#define y second
//涉及到图像连通性的问题,一般都是用并查集(本题中正向不好删除,采用反向方法不断扩大并查集),这要求离线操作(先存起来) 

//邻接表可以用数组方法,也可以用vector的方法比较方便 
const int N=50010;
int n,m,d;

vector<int>g[N];
vector<pii>dele;//删除的记录cq
vector<pii>query[1100];//记录每一组询问 

int f[N];//并查集 
bool st[N];//判断最一开始剩下那些点 


int find(int x){
	if(f[x]==x)return x;
	return find(f[x]);//find可以找到最顶端 
}
void merge(int x,int y){
	f[find(x)]=f[find(y)];//祖宗赋值到另一祖宗上 
	return ;
}


int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>d;
	int temp1,temp2;
	//读入线路 
	for(int i=1;i<=m;i++){
	   cin>>temp1>>temp2;
	   g[temp1].push_back(temp2);
	   g[temp2].push_back(temp1);//双向边 
	}
	//读入新增和询问 
	for(int i=0;i<d;i++){
		cin>>temp1>>temp2;
		st[temp1]=true;
		dele.push_back({temp1,temp2});
		while(temp2--){
			int x1,x2;
			cin>>x1>>x2;
			query[i].push_back({x1,x2});
		}
	}
	//并查集初始化 
	for(int i=1;i<=n;i++)f[i]=i;
	
	for(int i=1;i<=n;i++)//遍历n个点(邻接表存储) 
	{
	   if(!st[i]){
	   	 for(int j=0;j<g[i].size();j++)
	   	   if(!st[g[i][j]])merge(i,g[i][j]);
	   } 
	} 
	//依次增加并且扩大并查集(并查集只能增) 
	int ans[1100];
	memset(ans,0,sizeof(ans));
	for(int i=d-1;i>=0;i--){
		
		int c=dele[i].x;
		int q=dele[i].y;
		for(int j=0;j<q;j++){
			int fr=query[i][j].x;
			int to=query[i][j].y;
			if(find(fr)!=find(to))ans[i]++;//祖先不同,不连通 
		}
		//恢复该点
		st[c]=false;
		for(int j=0;j<g[c].size();j++){
		   if(!st[g[c][j]])merge(c,g[c][j]);	
		} 
	}
	for(int i=0;i<d;i++)cout<<ans[i]<<endl;
	return 0;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值