poj 2942 点双连通分量
1 /* 2 题意:有n个骑士,某些其实之间hate each other,互相hate的其实不能相邻而坐,然后会开多次圆桌会议, 3 每次从n个骑士中抽出3个或以上的奇数个骑士参加会议并且围着圆桌而坐,某些人每次会议都无法参加,这样 4 找出这些人的数目。 5 6 题解:点双连通(代码参考:http://blog.csdn.net/tsaid/article/details/6895808) 7 将可以相邻而坐的两个骑士加上边,最终得出的图中,找出其中的点双连通分量,只要这个点双连通分量包含 8 奇圈,这样这个分量中的任意点都可以找到属于的一个奇圈(可以这样思考:只要有奇圈,则该圈就一定会有两 9 个点出和入,因为只有一个点的话就是割点了,然后这两个点将该圈分成了奇数条边和偶数条边,这样就可以 10 通过自己选择其中一条路径而决定总路径为奇数圈或者偶数圈),再用二分图的性质找奇数圈,因为二分图不包 11 含奇数圈,因此通过染色来判断 12 */ 13 #include<iostream> 14 #include<cstdio> 15 #include<cstring> 16 17 using namespace std; 18 19 const int MAXV = 1005; 20 21 //最后得到的cut[]中的值为该点的分支数目,只有cut值大于等于2的点才是割点 22 int dfn[MAXV],low[MAXV],head[MAXV],cut[MAXV]; 23 bool vis[MAXV]; 24 int EN,cnt; 25 26 int temp[MAXV],stack[MAXV]; // temp记录双连通分量 27 int top,tnum; // tnum是temp数组点的数目 28 29 int block[MAXV],color[MAXV]; 30 int scc; 31 32 bool expell[MAXV]; // 记录哪个点必须删除 33 bool gra[MAXV][MAXV]; 34 35 struct Edge 36 { 37 int to,nxt; 38 }edge[MAXV*MAXV]; 39 40 void addedge(int cu,int cv) 41 { 42 edge[EN].to = cv; 43 edge[EN].nxt = head[cu]; 44 head[cu] = EN++; 45 } 46 47 bool odd_cycle(int u, int clr) 48 { 49 color[u] = clr; 50 for(int i=head[u]; i != -1; i = edge[i].nxt) 51 { 52 int v = edge[i].to; 53 if (block[v] == scc) // 相同连通分量的点才需要比较 54 { 55 if (color[v] != 0 && color[v] == color[u]) // 同色则表示为奇数圈 56 return true; 57 if (color[v] == 0 && odd_cycle(v,-clr)) // 没有染色则继续染色 58 return true; 59 } 60 } 61 return false; 62 } 63 64 void Tarjan(int u) // 求点双连通分量 65 { 66 vis[u] = true; 67 dfn[u] = low[u] = ++cnt; 68 stack[++top] = u; 69 for(int i = head[u]; i != -1; i = edge[i].nxt) 70 { 71 int v = edge[i].to; 72 if (dfn[v] == 0) { 73 Tarjan(v); 74 low[u] = min(low[u], low[v]); 75 // 此处当u不为根节点时,就是割点,为根节点时不为割点,但是由根节点和它的子节点组成的连通分量是双连通分量 76 if (low[v] >= dfn[u]) 77 { 78 cut[u]++; 79 scc++; 80 int t; 81 do 82 { 83 t = stack[top--]; 84 block[t] = scc; 85 temp[++tnum] = t; 86 } 87 while (t != v); 88 89 temp[++tnum] = u; 90 memset(color,0,sizeof(color)); 91 if (tnum >= 3 && odd_cycle(u,1)) 92 { 93 while (tnum != 0) 94 expell[temp[tnum--]] = false; 95 } 96 else 97 tnum = 0; 98 } 99 } 100 else if (vis[v]) 101 low[u] = min(low[u], dfn[v]); 102 } 103 } 104 105 int main(void) 106 { 107 int n,m; 108 while (~scanf("%d%d",&n,&m), n || m) 109 { 110 memset(gra,false,sizeof(gra)); 111 memset(block,0,sizeof(block)); 112 while (m--) 113 { 114 int k1,k2; 115 scanf("%d%d",&k1,&k2); 116 gra[k1][k2] = gra[k2][k1] = true; 117 } 118 memset(head,-1,sizeof(head)); 119 EN=0; 120 for(int i=1; i<n; i++) 121 for(int j=i+1; j<=n; j++) 122 if (!gra[i][j]) 123 { 124 addedge(i,j); 125 addedge(j,i); 126 } 127 128 memset(dfn,0,sizeof(dfn)); 129 memset(vis,false,sizeof(vis)); 130 for(int j=1; j<=n; j++) 131 { 132 cut[j] = 1; 133 expell[j] = true; 134 } 135 scc = 0; 136 for(int i=1; i<=n; i++) 137 { 138 if (!dfn[i]) 139 { 140 cnt = tnum = top = 0; 141 cut[i] = 0; // 当前以i为根结点搜索,因此需要赋0 142 Tarjan(i); 143 } 144 } 145 int ans = 0; 146 for(int i=1; i<=n; i++) 147 if (expell[i]) 148 ans++; 149 printf("%d\n",ans); 150 } 151 return 0; 152 }