Description
就是给一些极大团包含点的关系,使最少的极大团包含点数为奇数。
Solution
这里题目说明每个点只与两个极大团有被包含关系,所以我们把极大团看成点,点看成边,则每个点连出去所有的边(它们所对应的点)都与这个大点(所对应的极大团)有被包含关系。
于是我们搜索每个连通块,对于每个连通块我们建一颗搜索树(如下图,黑色边代表搜索树上的边)。
对于每一个大点,我们把所有连出去的边对应的未被匹配过的点的编号记录下来,若记录的点个数为奇数,则它与它父亲连接的边所对应的点就留给父亲匹配,然后挨个输出即可。
举个例子:
点7连出去两条边编号为7、8,且都未被匹配。则(7,8)是一个匹配。
点4连出去四条边,未被匹配的有3、4、6(这里假设5已被匹配),则(4,6)是一个匹配,而多余的3留给点4的父亲点2匹配。
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 20001
#define NN 400001
#define M 200001
using namespace std;
int m,n;
int to[NN],next[NN],last[NN],p[NN],num=0;
int z[N];
int zc[M];
bool pp[M];
void link(int x,int y,int t)
{
num++;
to[num]=y;
next[num]=last[x];
last[x]=num;
p[num]=t;
}
bool bz[N];
int rt;
int find(int x)
{
bz[x]=true;
int t=z[x];
for(int i=last[x];i;i=next[i])
{
int v=to[i];
if(!bz[v]) t+=find(v);
}
return t;
}
int g[M];
void dfs(int x,int fa)
{
bz[x]=true;
int t=0;
for(int i=last[x];i;i=next[i])
{
int v=to[i];
if(!bz[v]) dfs(v,p[i]);
}
for(int i=last[x];i;i=next[i])
if(!pp[p[i]] && p[i]!=fa) pp[p[i]]=true,g[++t]=p[i];
if(rt!=x) pp[fa]=true,g[++t]=fa;
int tt;
fo(i,1,t)
if(i%2==1) tt=g[i],pp[tt]=false;
else
{
pp[g[i]]=pp[tt]=true;
printf("%d %d\n",tt,g[i]);
}
}
int main()
{
cin>>m>>n;
fo(i,1,n)
{
int x,y;
scanf("%d %d",&x,&y);
z[x]++;
link(x,y,i);
link(y,x,i);
}
int ans=0;
fo(i,1,m)
if(!bz[i]) ans+=find(i)/2;
cout<<ans<<endl;
memset(bz,0,sizeof(bz));
fo(i,1,m)
if(!bz[i]) rt=i,dfs(i,0);
}