现在的位置: 首页 > 综合 > 正文

poj 2942 Knights of the Round Table 补图+点双连通分量+判定二分图

2018年01月11日 ⁄ 综合 ⁄ 共 2647字 ⁄ 字号 评论关闭

题意:给定n个点m条边。这些边的意思为 u和v是相互仇恨的关系

要求满足一下条件。

1)一个人的周围不能有仇恨关系的人,这些人围成一个圆圈,每个点有两个邻居。

2)会议的是由三个人以上组成,开会议的人数必须是奇数。

要求出必须剔除几个人。


在做本题的过程中我查询了一些其他博客的做法。

本题需要的知识

1)补图(已知G求~G)

2)奇圈的定义(顶点个数为奇数的圈,但也有部分人说是边的个数为奇数的圈)

3)两个定理

1.如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中;

2.如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。

可以通过下面这个例子来理解这两定理

1-->2

1-->3

1-->4

3-->4

2-->4

{1,2,3,4}是一个点双连通分量,它有偶数个点,但是它有两个奇圈,能出席两次会议

4)求点双连通分量(tarjan相关算法的知识)

5)二分图的判定(交叉染色法)

其他:显然在只要某个点双连通分量不是二分图那么它必定包含奇圈,那么这个点双连通分量中的所有点都可以出席会议。由于割点一定属于某个强连通分量,如果直接算的话可能会算重,所以标记,最后一起算。


更详细内容见:http://blog.csdn.net/lyy289065406/article/details/6756821#quote

//author: CHC
//First Edit Time:	2014-07-10 20:09
//Last Edit Time:	2014-07-11 09:09
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;
#define MAXN 1010
vector <int> e[MAXN];
vector <int> bck[MAXN];
//vector <int> e1[MAXN];
int dfn[MAXN],low[MAXN],sta[MAXN*2];
int times,top;
int ssum,n,m;
char map1[MAXN][MAXN];
char can[MAXN];
char vis[MAXN];
char inbck[MAXN];
void tarjan_4(int u){
    sta[++top]=u;
    dfn[u]=low[u]=++times;
    for(int i=0;i<(int)e[u].size();i++){
        int v=e[u][i];
        if(!dfn[v]){
            tarjan_4(v);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){
                do
                {
                    bck[ssum].push_back(sta[top]);
                }while(sta[top--]!=v);
                bck[ssum].push_back(u);
                ++ssum;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
bool checkdfs(int u,int color){
    vis[u]=color;
    for(int i=0;i<(int)e[u].size();i++){
        int v=e[u][i];
        if(!inbck[v])continue;
        if(vis[v]==-1){
            if(!checkdfs(v,!color))return false;
        }
        else if(vis[u]==vis[v])return false;
    }
    return true;
}
void solve(){
    ssum=top=times=0;
    for(int i=0;i<MAXN;i++)bck[i].clear();
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(can,0,sizeof(can));
    for(int i=1;i<=n;i++)
        if(!dfn[i])tarjan_4(i);

    for(int i=0;i<ssum;i++){
        int tmp=bck[i].size();
        //if(tmp<3||tmp%2==0)continue;
        //if(tmp<3)continue;
        //for(int j=0;j<n+10;j++)e1[j].clear();
        memset(inbck,0,sizeof(inbck));
        for(int j=0;j<tmp;j++){
            inbck[bck[i][j]]=1;
        }
        /*
        for(int j=0;j<tmp;j++){
            int u=bck[i][j];
            inbck[u]=1;
        for(int k=j+1;k<tmp;k++){
            int v=bck[i][k];
            if(u==v)continue;
            if(map1[u][v])continue;
            e1[u].push_back(v);
            e1[v].push_back(u);
            //printf("%d -->%d\n",u,v);
        }
        }
        */
        memset(vis,-1,sizeof(vis));
        if(!checkdfs(bck[i][0],0)){
            //printf("yes\n");
            for(int j=0;j<tmp;j++){
                can[bck[i][j]]=1;
                //printf("%d\n",bck[i][j]);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(!can[i])ans++;
    }
    printf("%d\n",ans);
}

int main()
{
    while(~scanf("%d%d",&n,&m)&&(n||m)){
        for(int i=0;i<n+10;i++)e[i].clear();
        memset(map1,0,sizeof(map1));
        for(int i=0,x,y;i<m;i++){
            scanf("%d%d",&x,&y);
            map1[x][y]=map1[y][x]=1;
        }
        for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if(!map1[i][j])
            {
                e[i].push_back(j);
                e[j].push_back(i);
                //printf("%d-->%d\n",i,j);
            }

        solve();
    }
    return 0;
}




抱歉!评论已关闭.