POJ2942 Knights of the Round Table 点双连通分量,逆图,奇圈
题目链接:
题意:
有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; }