POJ 2942 Knights of the Round Table

嘟嘟嘟

 

这道题debug了两个下午终于AC了(还是对拍好用),心中十分激动~~

首先不得不说,不看题解我是无论如何也想不出怎么建图的,刚开始直接按仇恨关系建图怎么搞也好不出来。

题解说,要建互补图!就是如果两个骑士能挨一块,就连一条边。然后tarjan求点双联通分量。为什么求点双联通分量呢?因为每一个会议都要围成一个环,而一个人不能同时参加两个会议,所以对于两个点u, v,要有至少两条完全不同的路能从u到v,那就是求点双联通分量了。

点双联通分量的求法和找割点很像,不过还有维护一个栈。如果这个节点第一次遍历,就放入栈中;如果low[v] >= dfn[u],说明u可能是割点(因为可能是根节点),但这并没有什么关系,这时候开始弹栈,直到st.top == v,因为u可能同时在多个点双中,所以u不能弹出,但还要把u加入这个点双中。记录所有点双,拿一个vector就行。

然后题中说,每一个会议参加的人数必须是奇数,所以我们要在点双联通分量里找奇环。有一个结论:如果一个点双联通分量中存在一个奇环,那么联通分量中任意一个点都在奇环上。这个好证:

假设奇环是x->B->y->A->x。然后对于两桶分量中的节点u:有两个环:u->x->B->y->u和u->x->A->y->u。又因为x->B->y和x->A->y中一定有一条路径是奇数的,那么包含u的两个环中一定有一个是奇环。

那么如何找奇环呢?判断二分图时有这么个结论:如果一张图不存在奇环,那么就是二分图。做法就是黑白染色,如果u走到得一个节点v已经被染过色了,且col[u] == col[v],那么就存在奇环。

然后如果这个联通分量中没有奇环,这些人就都无法参加任何一个会议。

还有一件事,这道题拿邻接矩阵存图比较方便一些。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 1e3 + 5;
 21 inline ll read()
 22 {
 23     ll ans = 0;
 24     char ch = getchar(), last = ' ';
 25     while(!isdigit(ch)) {last = ch; ch = getchar();}
 26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27     if(last == '-') ans = -ans;
 28     return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32     if(x < 0) x = -x, putchar('-');
 33     if(x >= 10) write(x / 10);
 34     putchar(x % 10 + '0');
 35 }
 36 
 37 int n, m;
 38 int v[maxn][maxn];
 39 
 40 stack<int> st;
 41 int dfn[maxn], low[maxn], cnt = 0;
 42 vector<int> vd[maxn];
 43 int cvd = 0;
 44 void tarjan(int now, int fa)        //求点双 
 45 {
 46     dfn[now] = low[now] = ++cnt;
 47     st.push(now); 
 48     for(int i = 1; i <= n; ++i)
 49     {
 50         if(v[now][i] && now != i)
 51         {
 52             if(!dfn[i])
 53             {
 54                 tarjan(i, now);
 55                 low[now] = min(low[now], low[i]);
 56                 if(low[i] >= dfn[now])
 57                 {
 58                     int x; ++cvd;
 59                     do
 60                     {
 61                         x = st.top(); st.pop();
 62                         vd[cvd].push_back(x);
 63                     }while(x != i);
 64                     vd[cvd].push_back(now);
 65                 }
 66             }
 67             else if(i != fa) low[now] = min(low[now], dfn[i]);            
 68         }
 69 
 70     }
 71 }
 72 
 73 bool vis[maxn];
 74 int mak[maxn], col[maxn];
 75 bool dfs(int now, int flg)        //染色 
 76 {
 77     mak[now] = flg;
 78     for(int i = 1; i <= n; ++i)
 79     {
 80         if(v[now][i] && now != i && col[now] == col[i])
 81         {
 82             if(mak[i] == -1) if(dfs(i, flg ^ 1)) return 1;                    
 83             if(mak[i] == mak[now]) return 1;
 84         }
 85     }
 86     return 0;
 87 }
 88 
 89 void init()
 90 {
 91     for(int i = 0; i < maxn; ++i)
 92         for(int j = 0; j < maxn; ++j) v[i][j] = 1;
 93     for(int i = 0; i < maxn; ++i) vd[i].clear();
 94     while(!st.empty()) st.pop();
 95     Mem(dfn, 0); Mem(low, 0);
 96     cnt = cvd = 0;
 97     Mem(vis, 0); Mem(col, 0);
 98 }
 99 
100 int main()
101 {
102     while(scanf("%d%d", &n, &m) && n && m)
103     {
104         init();
105         for(int i = 1; i <= m; ++i)
106         {
107             int x = read(), y = read();
108             v[x][y] = v[y][x] = 0;
109         }
110         for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i, 0);
111         for(int i = 1; i <= cvd; ++i)
112         {
113             Mem(mak, -1);
114             for(int j = 0; j < (int)vd[i].size(); ++j) col[vd[i][j]] = i;
115             if(dfs(vd[i][0], 0)) 
116                 for(int j = 0; j < (int)vd[i].size(); ++j) vis[vd[i][j]] = 1;
117         }
118         int ans = 0;
119         for(int i = 1; i <= n; ++i) ans += ((int)vis[i]) ^ 1;
120         write(ans); enter;
121     }
122 }
View Code
原文地址:https://www.cnblogs.com/mrclr/p/9674931.html