[省选前题目整理][POJ 2942]Knights of the Round Table(Tarjan求点双联通分量+DFS对环染色)

题目链接

http://poj.org/problem?id=2942

题目大意

有n个骑士要参加一个会议,其中有m对骑士互相憎恶,互相憎恶的骑士不能同时出席会议,n个骑士要坐在多个圆桌上,而且每桌至少要有3个骑士,每桌的骑士个数也必须是奇数个,问有多少骑士无论如何也不能出席会议。

思路

Tarjan算法求点双联通分支+DFS交叉染色法找奇环。

首先我们逆向思考此题的反问题:有多少骑士可能出席这个会议。我们对任意一对不互相憎恶的骑士连无向边,则一个骑士能出席会议当且仅当它处在某个简单奇环中,原问题转换成求有多少个点不在奇圈上。

那么,我们可以在这个图中求出它的所有点双联通分量,于是我们只需要在点双里找奇环了,点双上的奇环上的点都可以出席会议。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXE 2001000
#define MAXV 2000

using namespace std;

struct edge
{
    int u,v,next;
}edges[MAXE];

int head[MAXV],nCount=1;

void AddEdge(int U,int V)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

int n,m;
int stack[MAXE],top=0,dfn[MAXV],low[MAXV],dfs_cnt=0;
bool inStack[MAXV],vis[MAXE];
int belong[MAXV];
bool inbcc[MAXV],flag[MAXV]; //flag[i]=true表示点i可以参加会议

int color[MAXV];

bool DFS(int u)
{
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(inbcc[v]) //点v在点双里
        {
            if(color[v]==-1) //v还没被访问过
            {
                color[v]=(color[u]+1)%2;
                if(DFS(v)) return true;
            }
            else if(color[v]==color[u]) return true; //v已经访问过并且和u颜色相同,则找到了奇环
        }
    }
    return false;
}

void check(int u) //以u为起点检查奇环
{
    memset(color,-1,sizeof(color));
    color[u]=0;
    if(DFS(u))
    {
        for(int i=0;i<=n;i++)
            if(inbcc[i])
                flag[i]=true;
    }
}

void Tarjan_BCC(int u,int fa)
{
    low[u]=dfn[u]=++dfs_cnt;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(!dfn[v])
        {
            stack[++top]=p;
            Tarjan_BCC(v,u);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v])
            {
                memset(inbcc,false,sizeof(inbcc));
                int size=0; //该点双大小
                while(1)
                {
                    size++;
                    int tmp=stack[top--];
                    inbcc[edges[tmp].u]=inbcc[edges[tmp].v]=true;
                    if(edges[tmp].u==u&&edges[tmp].v==v) break;
                }
                if(size>=3) check(u); //若该点双大小>=3,则DFS对该点双进行黑白染色
            }
        }
        else if(dfn[v]<dfn[u]&&v!=fa) //该边为回边
        {
            stack[++top]=p;
            low[u]=min(low[u],dfn[v]);
        }
    }
}

int map[MAXV][MAXV];

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF&&!(!n&&!m))
    {
        nCount=0;
        dfs_cnt=0;
        top=0;
        memset(map,0,sizeof(map));
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(flag,false,sizeof(flag));
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            map[a][b]=map[b][a]=-1;
        }
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(map[i][j]==0) //i与j不互相仇恨
                {
                    AddEdge(i,j);
                    AddEdge(j,i);
                }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                Tarjan_BCC(i,-1);
        int sum=0;
        for(int i=1;i<=n;i++)
            sum+=flag[i];
        printf("%d\n",n-sum);
    }
    return 0;
}

你可能感兴趣的:([省选前题目整理][POJ 2942]Knights of the Round Table(Tarjan求点双联通分量+DFS对环染色))