5.17 写题

2022年江西省大学生程序设计竞赛(正式赛)

C.Graphic Game

给一个 2×n个点,m条边,没有重边和自环的无向图,每次可以选择两个度数相等的点删除,求删完所有点的方案

题面

思路:

首先 ,删除两个相同度数的点,必然会引起其他点的度数改变。所以用set<int>s[N]将度数相同的点维护起来,set<int>r 存储需要删除的度数,。直到r为空 ,判断s是不是都被删完了就可以(虽然从数学角度可以证明一定能删完,但是挺难想的,还是多写个)。

#include<bits/stdc++.h> 
using namespace std;

const int N = 1000100, INF = 0x3f3f3f3f,mod=1e9+7;

typedef pair<int, int>PII;


vector<int>e[N*2];
set<int>s[N];//记录度为i的点的集合
set<int>r;//记录可以删的点的度数,即入度相同的 
int n,m;
int du[N*2];//每个点的度数 
bool vis[N*2];
int x[N],y[N];
int d[5];//记录每次要删的两个点 

void dfs(int u)
{
	for(auto ed:e[u])
	{
		if(vis[ed]==0)
		{
			s[du[ed]].erase(ed);
			if(s[du[ed]].size()==1)
			{
				 r.erase(du[ed]);
			}//如果 某个点 大小小于2 就不用管了 
			du[ed]--;
			s[du[ed]].insert(ed);
			if(s[du[ed]].size()>=2) 
			{
				r.insert(du[ed]);
				//如果某个点 又大于2了 就继续删 
			}
			
			
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	 for(int i=0;i<m;i++)
	 {
	 	int u,v;
		 scanf("%d%d",&u,&v);
		 e[u].push_back(v);
		 e[v].push_back(u);
		 du[u]++;
		 du[v]++; 
	 }
	 
	for(int i=1;i<=2*n;i++)
	s[du[i]].insert(i);
	
	for(int i=0;i<=m;i++)
	{
		if(s[i].size()>=2) r.insert(i);
		//至少有两个才要删 
	}
	int ans=0;
	while(r.size())
	{
		int i;
		for(auto t:r)
		{
			i=t;
			break;
		}
		
		
		while(s[i].size()>=2)
		{
			int cnt=0;
			for(auto t:s[i])
			{
			 d[++cnt]=t;
			 if(cnt>=2) break; 
			}
			s[i].erase(d[1]);
			s[i].erase(d[2]); 
			vis[d[1]]=1,vis[d[2]]=1;
			dfs(d[1]);
			dfs(d[2]);
		x[++ans]=d[1];
		y[ans]=d[2];
		}
		if(r.find(i)!=r.end())
		r.erase(i); 
		
	}
	
	for(int i=0;i<=m;i++)
	if(s[i].size()!=0)
	{
		 printf(">_<");
		 return 0;
	}
	for(int i=1;i<=ans;i++)
	{
	
		printf("%d %d\n",x[i],y[i]);
	}
	
}

误区: 起初我是用vector 存图的,但交了发现内存超限 只能过80%,所以改用数组模拟;;

 

 搜了之后了解到 vector占用空间非常大,20000个元素就已经是数组的十倍了(哈人)

总结:以后存储图还是老老实实手打数组,不能贪图快了。

//代码是看第一的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值