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 }

 

posted @ 2014-04-10 11:50  辛力啤  阅读(222)  评论(0编辑  收藏  举报