POJ 2942 Knights of the Round Table(点双联通+二分图+染色)

大意:m个关系的人之间不能再圆桌上坐在相邻的位置上。并且人数为奇数,问至少踢出多少人能够开会。


思路:因为人是围着圆桌坐,所以每人的度肯定是2,即度为1和0(单独一人)的都要被踢除。那么可以抽象为点的双联通。在出现桥的时候(即度数为1)将该联通块的人都统计下来,判断是不是奇环(二分染色)(PS:说明为啥不能直接统计个数判断奇环呢?既然是点的双联通那么一定是环,是不是能够直接判断当前的人数就能下是奇环的定论呢? 答案是否定的!因为联通块可能由多个奇环组成!)


#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<stack>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL int
#define inf 0x3f3f3f3f
#define eps 1e-8
#include<vector>
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1

using namespace std;

const int Ma = 1100;
struct node{
    int to,w,next;
}q[Ma*Ma];
bool mp[1100][1100];
int head[Ma*Ma],dfn[Ma],stk[Ma*5],vis[Ma],low[Ma],tmp[Ma],col[Ma],part[Ma];
int cnt,top,tim,scc,sum,n,ans,mark[Ma];
void Add(int a,int b){
    q[cnt].to = b;
    q[cnt].next = head[a];
    head[a] = cnt++;
}

void init(){
    ans = scc =  cnt = top = 0;
    tim =  1;
    memset(head,-1,sizeof(head));
    memset(mp,false,sizeof(mp));
    for(int i = 1;i <= n;++ i){
        low[i]  = dfn[i] = 0;
        vis[i]=mark[i] = 0;
    }
}

bool ser(int u,int co){
    col[u] = co;
    for(int i=head[u];~i;i=q[i].next){
        int v = q[i].to;
        if(!part[v]) continue;
        if(col[v] == co)
            return true;
        if(!col[v] && ser(v,-co))
            return true;
    }
    return false;
}
void Judge(){
    memset(part,0,sizeof(part));
    memset(col,0,sizeof(col));
    for(int i = 0;i < sum;++ i){
        part[tmp[i] ] = 1;
    }
    if(ser(tmp[0],1)){
        for(int i = 0;i < sum;++ i)
            mark[tmp[i] ] = 1;
    }
}
void Tarjan(int u,int To){
    low[u] = dfn[u] = tim++;
    vis[u] = 1;
    stk[top++] = u;
    for(int i = head[u]; ~i ; i = q[i].next){
        int v = q[i].to;
        if(i == (To^1)) continue;
        if(!vis[v]){
            Tarjan(v,i);
            low[u] = min(low[u],low[v]);
            if(low[v] >= dfn[u]){
                sum = 0;
                tmp[sum++] = u;
                stk[top] = -1;
                while( stk[top] != v ){
                    tmp[sum++] = stk[--top];
                }
                Judge();

            }
        }
        else
            low[u] = min(low[u],dfn[v]);
    }
}

int main(){
    int m,i,j,k,a,b,c,cla;
    while(~scanf("%d%d",&n,&m)){
        if(!n&&!m) break;
        init();
        for(i = 0;i < m;++ i){
            scanf("%d%d",&a,&b);
            mp[b][a] = mp[a][b] = true;
        }

        for(i = 1;i <= n;++ i)
            for(j = i+1;j <= n;++ j)
                if(!mp[i][j])
                    Add(i,j), Add(j,i);

       for(i = 1;i <= n;++ i)
            if(!dfn[i])
                Tarjan(i,-1);
        for(i = 1;i <= n;++ i)
            if(mark[i])
                ans++;
        printf("%d\n",n-ans);
    }
    return 0;
}


你可能感兴趣的:(ACM,二分图,双联通分量,点的双联通)