POJ2942 Knights of the Round Table(点双连通分量 + 二分图染色)
题目大概说要让n个骑士坐成一圈,这一圈的人数要是奇数且大于2,此外有些骑士之间有仇恨不能坐在一起,问有多少个骑士不能入座。
双连通图上任意两点间都有两条不重复点的路径,即一个环。那么,把骑士看做点,相互不仇恨的骑士间连边,能坐在一圈骑士的肯定在同一个点双连通分量上。
不过还有个条件是人数要大于2:
- 有这么一个结论:如果一个双连通分量存在奇圈(点数为奇数的环),那么这个双连通分量里所有点一定会包含在某一个奇圈内。
- 大概是因为,双连通分量里面点为奇数个显然都包含在奇圈里;而如果是偶数个,一部分就包含在那个找到的奇圈,而剩下的偶数-奇数=奇数个点一定也形成一个奇圈。
- 这样只要判断双连通分量是否存在奇圈即可。而存在奇圈是图是否为二分图的充分必要条件,而判断是否为二分图可以用染色法。
因此这题就是找双连通分量,找到一个双连通分量后,染色判断它是否包含奇圈,如果是双连通分量内所有点都能入座。
要注意的是一个点能属于多个点双连通分量。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 1111 6 #define MAXM 1111111 7 struct Edge{ 8 int v,flag,next; 9 }edge[MAXM<<1]; 10 int NE,head[MAXN]; 11 void addEdge(int u,int v){ 12 edge[NE].v=v; edge[NE].flag=0; 13 edge[NE].next=head[u]; head[u]=NE++; 14 } 15 16 int n,ans[MAXN]; 17 18 int dn,dfn[MAXN],low[MAXN]; 19 int stack[MAXM],top; 20 int tag[MAXN],color[MAXN]; 21 22 bool dfs(int u){ 23 for(int i=head[u]; i!=-1; i=edge[i].next){ 24 int v=edge[i].v; 25 if(!tag[v]) continue; 26 if(color[v]==color[u]) return 1; 27 if(color[v]==-1){ 28 color[v]=!color[u]; 29 return dfs(v); 30 } 31 } 32 return 0; 33 } 34 35 void tarjan(int u){ 36 dfn[u]=low[u]=++dn; 37 for(int i=head[u]; i!=-1; i=edge[i].next){ 38 if(edge[i].flag) continue; 39 edge[i].flag=edge[i^1].flag=1; 40 stack[++top]=i; 41 42 int v=edge[i].v; 43 44 if(dfn[v]){ 45 low[u]=min(low[u],dfn[v]); 46 continue; 47 } 48 49 tarjan(v); 50 low[u]=min(low[u],low[v]); 51 52 if(low[v]>=dfn[u]){ 53 memset(tag,0,sizeof(tag)); 54 int k; 55 do{ 56 k=stack[top--]; 57 tag[edge[k].v]=1; 58 tag[edge[k^1].v]=1; 59 }while(edge[k^1].v!=u); 60 memset(color,-1,sizeof(color)); 61 color[u]=0; 62 if(dfs(u)){ 63 for(int i=1; i<=n; ++i){ 64 if(tag[i]) ans[i]=1; 65 } 66 } 67 } 68 } 69 } 70 71 bool ishate[MAXN][MAXN]; 72 int main(){ 73 int m; 74 while(~scanf("%d%d",&n,&m) && (n||m)){ 75 memset(ishate,0,sizeof(ishate)); 76 int a,b; 77 while(m--){ 78 scanf("%d%d",&a,&b); 79 ishate[a][b]=ishate[b][a]=1; 80 } 81 82 NE=0; 83 memset(head,-1,sizeof(head)); 84 for(int i=1; i<=n; ++i){ 85 for(int j=i+1; j<=n; ++j){ 86 if(i==j) continue; 87 if(!ishate[i][j]){ 88 addEdge(i,j); 89 addEdge(j,i); 90 } 91 } 92 } 93 94 memset(ans,0,sizeof(ans)); 95 dn=0; memset(dfn,0,sizeof(dfn)); 96 top=0; 97 for(int i=1; i<=n; ++i){ 98 if(dfn[i]==0) tarjan(i); 99 } 100 101 int res=0; 102 for(int i=1; i<=n; ++i){ 103 if(!ans[i]) ++res; 104 } 105 printf("%d\n",res); 106 } 107 return 0; 108 }