zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    LeetCode "Super Ugly Number" !
    LeetCode "Count of Smaller Number After Self"
    LeetCode "Binary Tree Vertical Order"
    LeetCode "Sparse Matrix Multiplication"
    LeetCode "Minimum Height Tree" !!
    HackerRank "The Indian Job"
    HackerRank "Poisonous Plants"
    HackerRank "Kundu and Tree" !!
    LeetCode "Best Time to Buy and Sell Stock with Cooldown" !
    HackerRank "AND xor OR"
  • 原文地址:https://www.cnblogs.com/mrclr/p/9674931.html
Copyright © 2011-2022 走看看