Knights of the Round Table
题意:n个人开会,有些人相互憎恨不能挨着坐,且每场会议要保证是奇数个人。问有几个人一场会议也不能参加。
【无向图的点的双连通分量+二分图的判定】
不相互憎恨的人连边建图。
题目本质上要求的是不在任何一个奇圈上的人有几个
先求出所有的点双连通分量,如果该分量不是二分图说明有奇圈,有奇圈就说明每个人都可以参加至少一场会议(p317有证明,不难),把这些人标记,最后拿总人数减去这些人就是答案了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxv=1010; 4 const int maxe=1000010; 5 6 int n,m; 7 8 int g[maxv][maxv]; 9 struct Edge{ 10 int u,v,nex; 11 }e[maxe<<1]; 12 int head[maxv]; 13 int cnt=0; 14 void add(int u,int v){ 15 e[cnt].u=u; 16 e[cnt].v=v; 17 e[cnt].nex=head[u]; 18 head[u]=cnt++; 19 } 20 void init(){ 21 memset(g,0,sizeof(g)); 22 memset(head,-1,sizeof(head)); 23 cnt=0; 24 } 25 26 int pre[maxv],iscut[maxv],bccno[maxv],dfsk,bcc_cnt; 27 vector<int> bcc[maxv]; 28 stack<int> s; 29 30 int dfs(int u,int f){ 31 int lowu=pre[u]=++dfsk; 32 int child=0; 33 for(int i=head[u];i!=-1;i=e[i].nex){ 34 int v=e[i].v; 35 if(!pre[v]){ 36 s.push(i); 37 child++; 38 int lowv=dfs(v,u); 39 lowu=min(lowu,lowv); 40 if(lowv>=pre[u]){ 41 iscut[u]=1; 42 bcc_cnt++; 43 bcc[bcc_cnt].clear(); //bcc从1开始编号 44 for(;;){ 45 int te=s.top(); 46 s.pop(); 47 Edge p=e[te]; 48 if(bccno[p.u]!=bcc_cnt) {bcc[bcc_cnt].push_back(p.u);bccno[p.u]=bcc_cnt; } 49 if(bccno[p.v]!=bcc_cnt) {bcc[bcc_cnt].push_back(p.v);bccno[p.v]=bcc_cnt; } 50 if(p.u==u&&p.v==v) break; 51 } 52 } 53 }else if(pre[v]<pre[u]&&v!=f){ 54 s.push(i); 55 lowu=min(lowu,pre[v]); 56 } 57 } 58 if(f<0&&child==1) iscut[u]=0; 59 return lowu; 60 } 61 62 void find_bcc(int n) 63 { 64 memset(pre,0,sizeof(pre)); 65 memset(iscut,0,sizeof(iscut)); 66 memset(bccno,0,sizeof(bccno)); 67 dfsk=bcc_cnt=0; 68 for(int i=0;i<n;i++) if(!pre[i]) 69 dfs(i,-1); 70 } 71 72 int odd[maxv]; //是否可以参加会议 73 int color[maxv]; 74 bool bipartite(int u,int b){ 75 for(int i=head[u];i!=-1;i=e[i].nex){ 76 int v=e[i].v; 77 if(bccno[v]!=b) continue; 78 if(color[u]==color[v]) return false; 79 if(!color[v]){ 80 color[v]=3-color[u]; 81 if(!bipartite(v,b)) return false; 82 } 83 } 84 return true; 85 } 86 int main(){ 87 while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){ 88 init(); 89 int u,v; 90 for(int i=0;i<m;i++){ 91 scanf("%d%d",&u,&v); 92 u--;v--; 93 g[u][v]=g[v][u]=1; 94 } 95 for(int i=0;i<n;i++) 96 for(int j=i+1;j<n;j++) if(!g[i][j]){ 97 add(i,j); 98 add(j,i); 99 } 100 find_bcc(n); 101 102 memset(odd,0,sizeof(odd)); 103 for(int i=1;i<=bcc_cnt;i++){ 104 memset(color,0,sizeof(color)); 105 for(int j=0;j<bcc[i].size();j++) bccno[bcc[i][j]]=i; //这一步是因为可能有的点属于不止一个点双连通分量 106 int u=bcc[i][0]; 107 color[u]=1; 108 if(!bipartite(u,i)) 109 for(int j=0;j<bcc[i].size();j++) odd[bcc[i][j]]=1; 110 } 111 int ans=n; 112 for(int i=0;i<n;i++) if(odd[i]) ans--; 113 printf("%d ",ans); 114 } 115 return 0; 116 }
下面这份代码有问题,,注释里写了
1 #include <iostream> 2 #include <cstring> 3 #include <stack> 4 #include <vector> 5 #include <cstdio> 6 using namespace std; 7 const int maxv=1010; 8 int A[maxv][maxv]; 9 int n,m; 10 struct Edge 11 { 12 int u,v,nex; 13 }e[maxv*maxv<<1]; 14 int head[maxv]; 15 int cnt=0; 16 void init() 17 { 18 memset(head,-1,sizeof(head)); 19 cnt=0; 20 } 21 void add(int u,int v) 22 { 23 e[cnt].u=u; 24 e[cnt].v=v; 25 e[cnt].nex=head[u]; 26 head[u]=cnt++; 27 } 28 29 int pre[maxv],iscut[maxv],bccno[maxv],dfsk,bcc_cnt; 30 stack<int> s; 31 vector<int> bcc[maxv]; 32 33 int dfs(int u,int id) 34 { 35 int lowu=pre[u]=++dfsk; 36 int child=0; 37 for(int i=head[u];i!=-1;i=e[i].nex) 38 { 39 int v=e[i].v; 40 if(i==(id^1)) continue; 41 if(!pre[v]) 42 { 43 child++; 44 s.push(i); 45 int lowv=dfs(v,id); 46 lowu=min(lowv,lowu); 47 if(lowv>=pre[u]) // 说明u是割顶 48 { 49 iscut[u]=1; 50 bcc_cnt++; 51 bcc[bcc_cnt].clear(); 52 for(;;) 53 { 54 Edge p=e[s.top()]; 55 s.pop(); 56 if(bccno[p.u]!=bcc_cnt) {bcc[bcc_cnt].push_back(p.u);bccno[p.u]=bcc_cnt;} 57 if(bccno[p.v]!=bcc_cnt) {bcc[bcc_cnt].push_back(p.v);bccno[p.v]=bcc_cnt;} 58 if(p.v==v&&p.u==u) break; 59 } 60 } 61 } 62 else if(pre[v]<pre[u]) //不加这一句就会错,,无解。。pre[v]难道不是一定小于pre[u]吗?? 63 { 64 s.push(i); 65 lowu=min(lowu,pre[v]); 66 } 67 } 68 if(id<0&&child==1) iscut[u]=0; 69 return lowu; 70 } 71 72 void find_bcc(int n) 73 { 74 memset(pre,0,sizeof(pre)); 75 memset(bccno,0,sizeof(bccno)); 76 memset(iscut,0,sizeof(iscut)); 77 dfsk=bcc_cnt=0; 78 for(int i=0;i<n;i++) if(!pre[i]) dfs(i,-1); 79 } 80 int odd[maxv],color[maxv]; 81 int bigraph(int u,int b) 82 { 83 for(int i=head[u];i!=-1;i=e[i].nex) 84 { 85 int v=e[i].v; 86 if(bccno[v]!=b) continue; 87 if(color[v]==color[u]) return 0; 88 if(!color[v]) 89 { 90 color[v]=3-color[u]; 91 if(!bigraph(v,b)) return 0; 92 } 93 } 94 return 1; 95 } 96 int main() 97 { 98 while(scanf("%d%d",&n,&m)&&(n||m)) 99 { 100 init(); 101 int u,v; 102 memset(A,0,sizeof(A)); 103 for(int i=0;i<m;i++) 104 { 105 scanf("%d%d",&u,&v); 106 u--;v--; 107 A[u][v]=A[v][u]=1; 108 } 109 for(int i=0;i<n;i++) 110 for(int j=i+1;j<n;j++) if(!A[i][j]) 111 { 112 add(i,j); 113 add(j,i); 114 } 115 116 find_bcc(n); 117 118 memset(odd,0,sizeof(odd)); 119 for(int i=1;i<=bcc_cnt;i++) 120 { 121 memset(color,0,sizeof(color)); 122 for(int j=0;j<bcc[i].size();j++) bccno[bcc[i][j]]=i; //有可能某个点属于多个环 123 int u=bcc[i][0]; 124 color[u]=1; 125 if(!bigraph(u,i)) //不是二分图----有奇环----都可参加至少一次会议 126 for(int j=0;j<bcc[i].size();j++) odd[bcc[i][j]]=1; 127 } 128 int ans=n; 129 for(int i=0;i<n;i++) 130 if(odd[i]) ans--; 131 printf("%d ",ans); 132 } 133 }
问题解决了,,pre[v]不一定小于pre[u],
在一个环里,如果顺时针dfs的话,最后会有pre[v]<pre[u],然后倒推回去会把这个环加到同一个新的连通分量里
接下来,按dfs接着走,会逆时针再去访问这个环,这时候会有pre[v]>pre[u],这种情况其实已经不能再把那条边压到栈里了,因为这个环已经记录过了,不用再处理