【POJ2942】Knights of the Round Table-点双连通分量+判断奇环

测试地址:Knights of the Round Table
题目大意:n个骑士,有些骑士之间相互仇恨,现在要选出圆桌骑士,要求若干个骑士围坐在圆桌旁,并且每个骑士不仇恨他的相邻两个骑士,并且选出的骑士数目为奇数,且不能只选出一个骑士,如果一个骑士在任何情况下都不能成为圆桌骑士,那么他就会被驱逐,求要被驱逐的骑士数目。
做法:这一题需要用到点双连通分量+判断奇环。
题目给了我们骑士之间的仇恨关系,考虑到这样不好做,而数据范围不很大(n1000),所以我们反过来,以骑士为节点,如果两个骑士之间不相互仇恨就连一条无向边。那么问题就变成了在这个图上找一个点数大于1的奇环,求不可能在奇环中的点数。那么我们可以直接求可能在奇环中的点数。
这里有一个结论:如果一个点双连通分量中存在奇环,那么整个点双连通分量中的点都可能处在奇环中。证明如下:假设一个点双连通分量中存在奇环,那么对于任意一个奇环外的点,向奇环中的任意两点分别连一条路径,因为它们同属一个点双连通分量,所以存在两条路径使它们不包含相同的点或边(除了选定的那个点),那么这两条路径就可以合为从奇环上一个点到另一个点的路径,而这两个点将奇环分为两条路径,一条有偶数条边,一条有奇数条边,那么只要根据新增路径边数的奇偶性选择删去哪一个部分,总能找到一个包含任意一个原奇环外的点的奇环,这样就证明了上面的结论。有了这个结论,我们就可以先用Tarjan算法求出图的点双连通分量,然后对于每个点双连通分量,判断这个点双连通分量有没有奇环即可。
怎么判断一个图有没有奇环呢?我们知道,二分图就是不存在奇环的图,那么我们只需对这个图进行2-染色,如果染完后有两个相邻的点颜色相同,那么这个图就存在奇环,反之就不存在。
最后还有一个问题,因为割点可以从属于多个点双连通分量,所以需要去重,去重的方法有很多,这里就不细说了。这样我们就解决了这一道题目。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,tot,tim,first[1010],dfn[1010],low[1010],s[1010],p[1010],ans,rt;
int stack[1010]={0},top;
bool g[1010][1010];
struct edge {int v,next;} e[2000010];
bool vis[1010],vis2[1010],c[1010],flag;

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs(int v)
{
    vis[v]=1;
    dfn[v]=++tim;
    low[v]=dfn[v];
    for(int i=first[v];i;i=e[i].next)
    {
        if (!vis[e[i].v])
        {
            dfs(e[i].v);
            if (low[e[i].v]>=dfn[v]) c[v]=1;
            low[v]=min(low[v],low[e[i].v]);
        }
        else low[v]=min(low[v],dfn[e[i].v]);
    }
}

void tarjan()
{
    memset(vis,0,sizeof(vis));
    memset(c,0,sizeof(c));
    tim=0;
    for(int i=1;i<=n;i++)
        if (!vis[i]) dfs(i);
}

int color(int v)
{
    vis2[v]=1;
    stack[++top]=v;
    int siz=1;
    for(int i=first[v];i;i=e[i].next)
    {
        if (!vis2[e[i].v])
        {
            s[e[i].v]=(s[v]+1)%2;
            if (!c[v]||low[e[i].v]<dfn[v]) siz+=color(e[i].v);
        }
        else if (s[e[i].v]==s[v]) flag=1;
    }
    return siz;
}

void find(int v)
{
    vis[v]=1;
    if (c[v])
    {
        s[v]=0;
        vis2[v]=1;
        int cnt=0;
        for(int i=first[v];i;i=e[i].next)
            if (low[e[i].v]>=dfn[v])
            {
                flag=0;top=0;
                s[e[i].v]=1;
                int siz=color(e[i].v)+1;
                if (flag&&siz>2)
                {
                    cnt++;
                    ans+=siz;
                    p[v]++;
                    for(int i=1;i<=top;i++)
                        p[stack[i]]++;
                }
            }
        ans-=max(p[v]-1,0);
    }
    for(int i=first[v];i;i=e[i].next)
        if (!vis[e[i].v]) find(e[i].v);
}

void solve()
{
    memset(vis,0,sizeof(vis));
    memset(vis2,0,sizeof(vis2));
    memset(p,0,sizeof(p));
    memset(s,-1,sizeof(s));
    ans=0;
    for(int i=1;i<=n;i++)
        if (!vis[i]) rt=i,find(i);
}

int main()
{
    while(scanf("%d%d",&n,&m)&&n)
    {
        memset(first,0,sizeof(first));
        memset(g,0,sizeof(g));
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            g[a][b]=g[b][a]=1;
        }
        tot=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if (!g[i][j]&&i!=j) insert(i,j);

        tarjan();
        solve();

        printf("%d\n",n-ans);
    }

    return 0;
}
posted @ 2017-08-17 20:31  Maxwei_wzj  阅读(117)  评论(0编辑  收藏  举报