Knights of the Round Table UVALive

Knights of the Round Table

 UVALive - 3523

题意: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 }
View Code

  下面这份代码有问题,,注释里写了

  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 }
View Code

问题解决了,,pre[v]不一定小于pre[u],

在一个环里,如果顺时针dfs的话,最后会有pre[v]<pre[u],然后倒推回去会把这个环加到同一个新的连通分量里

接下来,按dfs接着走,会逆时针再去访问这个环,这时候会有pre[v]>pre[u],这种情况其实已经不能再把那条边压到栈里了,因为这个环已经记录过了,不用再处理

原文地址:https://www.cnblogs.com/yijiull/p/7390246.html