• POJ2942 Knights of the Round Table 点双连通分量,逆图,奇圈


    题目链接:

    poj2942




    题意:

    有n个人,能够开多场圆桌会议

    这n个人中,有m对人有仇视的关系,相互仇视的两人坐在相邻的位置

    且每场圆桌会议的人数仅仅能为奇书

    问有多少人不能參加




    解题思路:

    首先构图,将全部的仇视关系视为一条边,最后再取已经得到的图的逆图,

    这样图上连接的边就代表能够相邻而坐的关系

    然后就是找奇圈了,首先就是要找图中的环(点双连通分量)

    这个环为奇环的条件:不是二分图||这个环中的部分点属于其它奇环

    这个推断能够通过将已找到的环进行dfs黑白染色推断

    最后不在奇环内的总和即是答案


    至于为什么要找的是点双连通分量而不是边双连通分量 能够试试这组数据:

    6 8
    1 4
    1 5
    1 6
    2 4
    2 5
    2 6
    3 6
    4 5
    0 0

    得到的逆图是这种:

    假设是电双连通分量(拆开割点)则分为(1 2 3)和(3 4 5 6)两部分

    而假设是边双连通分量(去掉割边)则仅仅有(1 2 3 4  5 6 )一部分了


    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define maxn 1050
    using namespace std;
    struct node{
        int to,next;
    }edge[2000500];
    int head[maxn],ss;
    int map[maxn][maxn];
    
    int in[maxn],odd[maxn],temp[maxn];
    int color[maxn];
    
    int dfn[maxn],low[maxn],num;
    int insta[maxn],sta[maxn],top;
    int n;
    
    void init()
    {
        memset(dfn,0,sizeof(dfn));
        memset(head,-1,sizeof(head));
        memset(insta,0,sizeof(insta));
        memset(map,0,sizeof(map));
        memset(odd,0,sizeof(odd));
        top=num=ss=0;
    }
    
    void addedge(int a,int b)
    {
        edge[ss]=(node){b,head[a]};
        head[a]=ss++;
    }
    
    int dfs(int u,int c)
    {
        color[u]=c;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!in[v])
                continue;
            if(color[v]==c)
                return 1;
            else if(color[v])
                continue;
            else if(dfs(v,3-c))
                return 1;
        }
        return 0;
    }
    
    void Tarjan(int u,int pre)
    {
        dfn[u]=low[u]=++num;
        insta[u]=1;
        sta[top++]=u;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==pre)
                continue;
            if(!dfn[v])
            {
                Tarjan(v,u);
                low[u]=min(low[u],low[v]);
    
                if(dfn[u]<=low[v])
                {
                    int s=0,d=-1;
                    memset(in,0,sizeof(in));
                    while(d!=v)         //注意是v   点双连通的还有一种写法,总之要注意割点能够属于多个连通分量
                    {
                        d=sta[--top];
                        in[d]=1;
                        insta[d]=0;       //不能让u=0
                        temp[s++]=d;
                    }
                    in[u]=1;
                    memset(color,0,sizeof(color));
                    if(dfs(u,1))          //黑白染色判定
                    {
                        odd[u]=1;
                        while(s!=0)
                            odd[temp[--s]]=1;
                    }
                }
            }
            else if(insta[v])
                low[u]=min(low[u],dfn[v]);
        }
    }
    
    void solve()
    {
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                Tarjan(i,-1);
        int ans=0;
        for(int i=1;i<=n;i++)
            if(!odd[i])
                ans++;
        printf("%d
    ",ans);
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int m,a,b;
        while(scanf("%d%d",&n,&m)&&(m+n))
        {
            init();
            while(m--)
            {
                scanf("%d%d",&a,&b);
                map[a][b]=map[b][a]=1;
            }
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    if(i!=j&&!map[i][j])   //取逆图
                        addedge(i,j);
            solve();
        }
        return 0;
    }
    


  • 相关阅读:
    Gson简要使用笔记
    android入门到熟练(五)---广播
    95&&96.Unique Binary Search Trees I&&II
    day 08 文件操作
    07 深浅拷贝
    06 day小数据池
    05,.字典,集合
    列表和元组
    字符串
    while 循环,格式化输出和运算编码
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/6905489.html
Copyright © 2020-2023  润新知