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 }