POJ 2942Knights of the Round Table(二分图判定+双连通分量)
题意:一些骑士,他们有些人之间有矛盾,现在要求选出一些骑士围成一圈,圈要满足如下条件:1.人数大于1。2.总人数为奇数。3.有仇恨的骑士不能挨着坐。问有几个骑士不能和任何人形成任何的圆圈。
分析:以骑士为借点建立无向图G,如果两个骑士可以相邻(即不相互憎恶)建立一条有向边,题目转化成求不在任意一个简单奇圈上(包含奇数个节点的回路)的节点个数,
对于每个双连通分量,看是否存在奇环,若存在那么这个双连通分量中的任意两骑士都可以同时出现在一个奇环里:训练指南P317解释, 双联通分量B中有一个奇环C,奇环C中含有u1和u2两个点,v属于连通分量B,那么v一定存在到达u1和u2的路径,在奇环C中,u1和u2的两条路是一奇一偶的,由于v到u1和u2的两条路径此时必然能够造成一个经过v的奇环
一个奇环就是一场会议
二分图是没有奇环的
主算法:对于每个连通分量的每个双连通分量B,若它不是二分图,给B中所有节点标记为在 在奇环上,
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <stack> 6 #include <vector> 7 using namespace std; 8 const int Max = 1010; 9 vector<int> G[Max], bcc[Max]; 10 int odd[Max], color[Max]; 11 int A[Max][Max]; 12 int pre[Max], iscut[Max], bccno[Max]; 13 int dfs_clock, bcc_cnt; 14 15 struct Edge 16 { 17 int u, v; 18 }; 19 stack<Edge> S; 20 int dfs(int u, int fa) 21 { 22 int lowu = pre[u] = ++dfs_clock; 23 int child = 0; 24 int Size = (int) G[u].size(); 25 for (int i = 0; i < Size; i++) 26 { 27 int v = G[u][i]; 28 Edge e; 29 e.u = u; 30 e.v = v; 31 if (!pre[v]) 32 { 33 S.push(e); 34 child++; 35 int lowv = dfs(v, u); 36 lowu = min(lowu, lowv); 37 if (lowv >= pre[u]) 38 { 39 iscut[u] = true; 40 ++bcc_cnt; 41 bcc[bcc_cnt].clear(); 42 for(;;) 43 { 44 Edge x = S.top(); 45 S.pop(); 46 if (bccno[x.u] != bcc_cnt) 47 { 48 bcc[bcc_cnt].push_back(x.u); 49 bccno[x.u] = bcc_cnt; 50 } 51 if (bccno[x.v] != bcc_cnt) 52 { 53 bcc[bcc_cnt].push_back(x.v); 54 bccno[x.v] = bcc_cnt; 55 } 56 if (x.u == u && x.v == v) 57 break; 58 } 59 } 60 } 61 else if (pre[v] < pre[u] && v != fa) 62 { 63 S.push(e); 64 lowu = min(lowu, pre[v]); 65 } 66 } 67 if (fa < 0 && child == 1) //这里之前不理解,因为在之前判断是割点的时候已经将这种情况当成了割点,但是对于连通分量来说没问题 68 iscut[u] = false; 69 return lowu; 70 } 71 void find_bcc(int n) 72 { 73 memset(pre, 0, sizeof(pre)); 74 memset(iscut, 0, sizeof(iscut)); 75 memset(bccno, 0, sizeof(bccno)); 76 dfs_clock = bcc_cnt = 0; 77 for (int i = 0; i < n; i++) 78 { 79 if (!pre[i]) //没访问就访问 80 dfs(i, -1); 81 } 82 } 83 bool bipartite(int u, int b) //判断二分图 84 { 85 for (int i = 0; i < (int) G[u].size(); i++) 86 { 87 int v = G[u][i]; 88 if (bccno[v] != b) 89 continue; 90 if (color[v] == color[u]) 91 return false; 92 if (!color[v]) 93 { 94 color[v] = 3 - color[u]; // 0表示没图,1表示1种颜色,2表示另一种 95 if (!bipartite(v, b)) 96 return false; 97 } 98 } 99 return true; 100 } 101 int main() 102 { 103 int n, m; 104 while (scanf("%d%d", &n, &m) != EOF) 105 { 106 if (n == 0 && m == 0) 107 break; 108 for (int i = 0; i <= n; i++) 109 G[i].clear(); 110 memset(A, 0, sizeof(A)); 111 for (int i = 0; i < m; i++) 112 { 113 int u, v; 114 scanf("%d%d", &u, &v); 115 u--; 116 v--; // 从0开始 117 A[u][v] = A[v][u] = 1; 118 } 119 for (int i = 0; i < n; i++) 120 { 121 for (int j = i + 1; j < n; j++) 122 { 123 if (!A[i][j]) 124 { 125 G[i].push_back(j); 126 G[j].push_back(i); 127 } 128 } 129 } 130 131 find_bcc(n); // 找连通分量 132 133 // cout << bcc[3][0] << endl; 134 memset(odd, 0, sizeof(odd)); 135 for (int i = 1; i <= bcc_cnt; i++) 136 { 137 memset(color, 0, sizeof(color)); 138 for (int j = 0; j < (int) bcc[i].size(); j++) 139 bccno[ bcc[i][j] ] = i; //主要处理割顶,一个割点可能属于多个连通分量 140 int u = bcc[i][0]; 141 color[u] = 1; 142 143 if (!bipartite(u, i)) //如果不是二分图就标记 144 { 145 for (int j = 0; j < (int) bcc[i].size(); j++) 146 odd[ bcc[i][j] ] = 1; 147 } 148 } 149 int ans = n; 150 for (int i = 0; i < n; i++) 151 if (odd[i]) //没标记的可定一场会议都参加不成 152 ans--; 153 printf("%d\n", ans); 154 } 155 return 0; 156 }