POJ - 2942 Knights of the Round Table (点双联通分量+二分图判定)

题意:有N个人要参加会议,围圈而坐,需要举手表决,所以每次会议都必须是奇数个人参加。有M对人互相讨厌,他们的座位不能相邻。问有多少人任意一场会议都不能出席。

分析:给出的M条关系是讨厌,将每个人视作点,在没有讨厌关系的人之间连边。

问题中很重要的一点是:任意一场会议都不能参加。能够参加某一场会议就意味着,参加会议的人可以构成一个奇回路(他们要围圈而坐,且必须出席奇数个人)。那么问题就转化成了求有多少个点不在任意一个奇回路中。

简单圈上的所有结点都属于同一个点双连通分量,因此找出所有的点双连通分量,并对该点双连通分量中判断其是否为二分图(二分图不含奇圈)。如果不是二分图,则表示其含有奇圈。打上标记,最后用点数N-没有被打上标记的点数,就是答案。

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<stack>
using namespace std;
const int maxn =1e3+5;
const int maxm = 1e6+5;
struct Edge{
    int to,next;
}edges[maxm<<1];
bool instack[maxn];
int bccno[maxn],head[maxn],dfn[maxn],low[maxn],clk,top,scc;
stack<int> S;
int col[maxn];
int ans;
bool check[maxn];
int tag[maxn];
bool can[maxn];

bool dfs(int u,int c)
{
    col[u]=c;
    for(int i=head[u]; i!=-1; i=edges[i].next){
        int v=edges[i].to;
        if(!check[v])
            continue;
        if(col[v]!=-1){
            if(col[v]==c)
                return false;
            continue;
        }
        if(!dfs(v,!c))
            return false;
    }
    return true;
}
void init()
{
    clk = top = scc = 0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(bccno,0,sizeof(bccno));
    memset(instack,0,sizeof(instack));
    memset(can,0,sizeof(can));
}

void AddEdge(int u,int v)
{
    edges[top].to = v;
    edges[top].next =head[u];
    head[u] = top++;
}

void Tarjan(int u,int id)
{
    int v;
    low[u]=dfn[u]=++clk;
    S.push(u);
    instack[u]=true;
    for(int i=head[u];~i;i=edges[i].next){
        v = edges[i].to;
        if(i==(id^1))   continue;
        if(!dfn[v]){
            Tarjan(v,i);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){     //找到一个双连通分量
                scc++;                  //从1开始
                int cnt=0;
                memset(check,0,sizeof(check));         //判断是否在同一个双连通分量内
                while(true){
                    int x =S.top();S.pop();
                    tag[cnt++] = x;
                    bccno[x]=scc;               //确定分量编号
                    instack[x]=false;
                    check[x] =true;
                    if(x==v)    break;      //找到了自己就要停止标号
                }
                check[u] = true;
                memset(col,-1,sizeof(col));          //判断双连通分量内是否有奇圈
                if(!dfs(u,0)){ 
                    can[u] = true;
                    while(cnt--)  can[tag[cnt]] = true;
                }
            }
        }
        else if(instack[v])
            low[u]= min(low[u],dfn[v]);
    }
    
}

int G[maxn][maxn];

int main()
{
    #ifndef ONLINE_JUDGE
         freopen("in.txt","r",stdin);
         freopen("out.txt","w",stdout);
    #endif
    int T,N,M,u,v,tmp;
    while(scanf("%d%d",&N,&M)==2){
        if(!N) break;  
        init();
        memset(G,0,sizeof(G));
        for(int i=1;i<=M;++i){
            scanf("%d%d",&u,&v);
            G[u][v] = G[v][u] = 1;
        }
        for(int i=1;i<=N;++i){
            for(int j=i+1;j<=N;++j){
                if(!G[i][j]){
                    AddEdge(i,j);
                    AddEdge(j,i);
                }
            }
        }
        for(int i=1;i<=N;++i){
            if(!dfn[i])
                Tarjan(i,-1);
        }
        ans= N;
        for(int i=1;i<=N;++i){
            if(can[i]) ans--;
        }
        printf("%d\n",ans);    
    }
    return 0;
}

 

posted @ 2018-08-01 10:47  xiuwenL  阅读(146)  评论(0编辑  收藏  举报