zoukankan      html  css  js  c++  java
  • 【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-ans);
        }
    
        return 0;
    }
    
  • 相关阅读:
    Scrapy的架构与原理的理解【转】
    Scrapy框架的命令行详解【转】
    WPF 程序中启动和关闭外部.exe程序
    C++ 二维数组(双重指针作为函数参数)
    C++ 遇见的一些函数
    C++ #pragma 预处理指令
    C++异常处理(Exception Handling)
    C++模板学习随笔
    C++ 数组的地址问题学习随笔
    关于C++几个容易混淆的概念总结
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793607.html
Copyright © 2011-2022 走看看