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个元素就已经是数组的十倍了(哈人)
总结:以后存储图还是老老实实手打数组,不能贪图快了。
//代码是看第一的