[POJ2942]Knights of the Round Table(点双连通分量+染色找奇环)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20190102/article/details/102724376

题目

Knights of the Round Table

题目大意

N N 个骑士,给出 M M 对仇恨关系,若 i i j j 有仇恨,他们就不能在圆桌会议上坐相邻的位置。请你求出有多少个骑士无法参加任何一个圆桌会议(例如但不仅限于,某个骑士与其他所有骑士有仇恨关系,他就不能参加任何一个圆桌会议)。要求每个圆桌会议必须由奇数个人参加。

分析

考虑原图的补图,即每个骑士可以和哪些骑士相邻。
一个圆桌会议显然对应的是图上的一个奇环(由奇数个点组成的环),所以我们要求的就是有哪些点不在任何一个奇环上。

下面证明两个引理。


引理一:如果两个点在不同的点双连通分量里面,它们就不可能在一个环里。

证明:点双连通分量是最大的不含割点的连通分量,如果两个点在一个环里,就可以把环
下图中的 A A B B 及割点一起合并起来,这样就形成了一个更大的点双连通分量,与条件矛盾。
图示1

引理二:一个点双连通分量只要有一个奇环,那么这里面的所有点都属于某个奇环。

证明:找到这个点双中的奇环,考虑某个不在这个奇环上的点,那它一定是这样连在这个奇环上的:
图示2
而不可能是这些情况(否则红点就成了割点):
图示3
(上图没有列举完其他情况,但是可以发现其他情况都是不可能的)

那么讨论一下链 i A j i\to A\to j 的长度:

扫描二维码关注公众号,回复: 7580058 查看本文章
  • 若为奇数:那么环 i A j x i\to A\to j\to x 是奇环;
  • 若为偶数:那么链 i B C j i\to B\to C\to j 的长度是偶数,则环 i B C j x i\to B\to C\to j\to x 是奇环。

得证。


有了这两个引理,我们只需要找所有点双,然后看里面有没有奇环即可。
找奇环的方法:搜索,黑白染色。

代码

#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

#define MAXN 1000
int N,M;
int Out[MAXN+5];
int Belong[MAXN+5];
bool T[MAXN+5][MAXN+5];
vector<int> G[MAXN+5];

int Color[MAXN+5];
int bfs(int S,int id){
    queue<int> Q;
    Q.push(S),Color[S]=1;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=0;i<int(G[u].size());i++){
            int v=G[u][i];
            if(Belong[v]!=id)
                continue;
            if(!Color[v]){
                Color[v]=-Color[u];
                Q.push(v);
            }
            else if(Color[v]==Color[u])
                return 1;
        }
    }
    return 0;
}

int cnt;
stack<int> Node;
int dfn[MAXN+5],Low[MAXN+5];
void dfs(int u,int fa){
    Node.push(u);
    Low[u]=dfn[u]=++cnt;
    for(int i=0;i<int(G[u].size());i++){
        int v=G[u][i];
        if(!dfn[v]){
            dfs(v,u);
            Low[u]=min(Low[u],Low[v]);
            if(Low[v]>=dfn[u]){
                vector<int> tmp;
                do{
                    tmp.push_back(Node.top()),Node.pop();
                }while(tmp.back()!=v);
                tmp.push_back(u);//注意找点双时不要把u点pop掉了
                for(int j=0;j<int(tmp.size());j++)
                    Belong[tmp[j]]=tmp[0];
                if(bfs(tmp[0],tmp[0]))
                    for(int j=0;j<int(tmp.size());j++)
                        Out[tmp[j]]=0;
                for(int j=0;j<int(tmp.size());j++)
                    Color[tmp[j]]=Belong[tmp[j]]=0;//要清零,因为一个点可以属于多个点双
            }
        }
        else if(v!=fa)
            Low[u]=min(Low[u],dfn[v]);
    }
}

int main(){
    while(scanf("%d%d",&N,&M),N&&M){
        memset(T,0,sizeof T);
        for(int i=1;i<=M;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            T[u][v]=T[v][u]=1;
        }
        for(int i=1;i<=N;i++)
            for(int j=i+1;j<=N;j++)
                if(!T[i][j])
                    G[i].push_back(j),
                    G[j].push_back(i);
        cnt=0;
        while(!Node.empty())
            Node.pop();
        memset(dfn,0,sizeof dfn);
        memset(Low,0,sizeof Low);
        memset(Color,0,sizeof Color);
        memset(Belong,0,sizeof Belong);
        for(int i=1;i<=N;i++)
            Out[i]=1;
        for(int i=1;i<=N;i++)
            if(!dfn[i])
                dfs(i,-1);
        int Ans=0;
        for(int i=1;i<=N;i++)
            Ans+=Out[i];
        printf("%d\n",Ans);
        for(int i=1;i<=N;i++)
            G[i].clear();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/102724376
今日推荐