题目链接:
题意:
有n个人,可以开多场圆桌会议
这n个人中,有m对人有仇视的关系,相互仇视的两人坐在相邻的位置
且每场圆桌会议的人数只能为奇书
问有多少人不能参加
解题思路:
首先构图,将所有的仇视关系视为一条边,最后再取已经得到的图的逆图,
这样图上连接的边就代表可以相邻而坐的关系
然后就是找奇圈了,首先就是要找图中的环(点双连通分量)
这个环为奇环的条件:不是二分图||这个环中的部分点属于其他奇环
这个判断可以通过将已找到的环进行dfs黑白染色判断
最后不在奇环内的总和即是答案
至于为什么要找的是点双连通分量而不是边双连通分量 可以试试这组数据:
6 8
1 4
1 5
1 6
2 4
2 5
2 6
3 6
4 5
0 0
得到的逆图是这样的:
如果是电双连通分量(拆开割点)则分为(1 2 3)和(3 4 5 6)两部分
而如果是边双连通分量(去掉割边)则只有(1 2 3 4 5 6 )一部分了
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1050
using namespace std;
struct node{
int to,next;
}edge[2000500];
int head[maxn],ss;
int map[maxn][maxn];
int in[maxn],odd[maxn],temp[maxn];
int color[maxn];
int dfn[maxn],low[maxn],num;
int insta[maxn],sta[maxn],top;
int n;
void init()
{
memset(dfn,0,sizeof(dfn));
memset(head,-1,sizeof(head));
memset(insta,0,sizeof(insta));
memset(map,0,sizeof(map));
memset(odd,0,sizeof(odd));
top=num=ss=0;
}
void addedge(int a,int b)
{
edge[ss]=(node){b,head[a]};
head[a]=ss++;
}
int dfs(int u,int c)
{
color[u]=c;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!in[v])
continue;
if(color[v]==c)
return 1;
else if(color[v])
continue;
else if(dfs(v,3-c))
return 1;
}
return 0;
}
void Tarjan(int u,int pre)
{
dfn[u]=low[u]=++num;
insta[u]=1;
sta[top++]=u;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)
continue;
if(!dfn[v])
{
Tarjan(v,u);
low[u]=min(low[u],low[v]);
if(dfn[u]<=low[v])
{
int s=0,d=-1;
memset(in,0,sizeof(in));
while(d!=v) //注意是v 点双连通的另一种写法,总之要注意割点可以属于多个连通分量
{
d=sta[--top];
in[d]=1;
insta[d]=0; //不能让u=0
temp[s++]=d;
}
in[u]=1;
memset(color,0,sizeof(color));
if(dfs(u,1)) //黑白染色判定
{
odd[u]=1;
while(s!=0)
odd[temp[--s]]=1;
}
}
}
else if(insta[v])
low[u]=min(low[u],dfn[v]);
}
}
void solve()
{
for(int i=1;i<=n;i++)
if(!dfn[i])
Tarjan(i,-1);
int ans=0;
for(int i=1;i<=n;i++)
if(!odd[i])
ans++;
printf("%d\n",ans);
}
int main()
{
// freopen("in.txt","r",stdin);
int m,a,b;
while(scanf("%d%d",&n,&m)&&(m+n))
{
init();
while(m--)
{
scanf("%d%d",&a,&b);
map[a][b]=map[b][a]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&!map[i][j]) //取逆图
addedge(i,j);
solve();
}
return 0;
}