Spoj 2878 KNIGHTS

题目链接

 

考虑建立原图的补图,即如果两个骑士不互相憎恨,就在他们之间连一条无向边。

显而易见的是,如果若干个骑士在同一个点数为奇数的环上时,他们就可以在一起开会。换句话说,如果一个骑士被一个奇环包含,那么他就一定可以去开会。

想到环,我们就可以考虑无向图的双联通分量。

当我们用Tarjan算法求出无向图上的双联通分量后再来考虑这一道题时,我们就可以得出两个结论:

1.如果两个骑士分别在两个不同的双联通分量里,那么他们就不可能在一起开会。

这一个结论是很明显的。因为将每个双联通分量缩点后,新图一定是一棵树,那么任意两个不同的双联通分量之间就不会存在环,连环都没有,就肯定不能在一起开会了。

2.如果在一个双联通分量里存在一个奇环,那么这一个双联通分量中的任意一个节点都至少会被一个奇环包含。

这一个证明起来也很容易。如图,节点A,B,C,D,E,F同属一个双联通分量,节点A,B,D,E,F构成了一个奇环。首先,对于奇环上的点,上述结论显然成立,我们要重点讨论的是奇环外的点,即节点C。此时,我们在这个环上取两个点A,B,那么,这一个环就相当于被分割成了两部分,一部分是D,另一部分是E,F。因为环的长度是奇数,所以这两部分的长度肯定是一奇一偶的。接下来,我们分别找一条从A到C的路径和一条从B到C的路径,且这两条路径在中途不相交,因为它们同属一个双联通分量,所以这样的两条路径一定存在。如果这两条路径的和为奇数的话,我们就拿这两条路径和环上偶数的那一部分接起来,否则就拿这两条路径和环上偶数的那一部分接起来。总之,无论怎么样,我们都能配出一个奇环将节点C包含。

有了这两条结论后,这道题做起来就很容易了。我们只需单独考虑每一个双联通分量,然后判断这一个双联通分量里是否有奇环即可,如果没有的话,这一个双联通分量里的骑士就都不能开会。那么应该如何判断是否有奇环呢?如果一个图里存在奇环,那它就一定不是一个二分图,否则它就一定是。也就是说,我们只要利用染色来判定某一个双联通分量是否为二分图即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
    using namespace std;
struct edge
{
    int last;
    int end;
}e[2000005];
    int ne=0,tot=0,cnt=0,top=0;
    int sta[1005],col[1005],dfn[1005],low[1005],note[1005];
    bool flag=false,mark[1005],book[1005],f[1005][1005];
    vector<int> dcc[1005];
void NewEdge(int u,int v)
{
    ne++;
    e[ne].last=note[u];
    e[ne].end=v;
    note[u]=ne;
}
void tarjan(int x)//求点双联通分量 
{
    dfn[x]=low[x]=++tot;
    sta[++top]=x;
    for(int i=note[x];i;i=e[i].last)
        if(!dfn[e[i].end])
        {
            tarjan(e[i].end);
            low[x]=min(low[x],low[e[i].end]);
            if(low[e[i].end]>=dfn[x]) 
            {
                cnt++;
                dcc[cnt].push_back(x);
                while(sta[top+1]!=e[i].end)
                    dcc[cnt].push_back(sta[top--]);
            }
        }
        else low[x]=min(low[x],dfn[e[i].end]);
}
void dfs(int x)//二分图判定 
{
    if(flag) return;
    for(int i=note[x];i;i=e[i].last) 
    {
        if(!mark[e[i].end]) continue;
        if(!col[e[i].end])
            col[e[i].end]=(col[x]==1)?2:1,dfs(e[i].end);
        else if(col[e[i].end]==col[x]) {flag=true;break;}
    }
}
int main()
{
    for(;;)
    {
        int n=0,m=0;
        scanf("%d%d",&n,&m);
        if(n==0&&m==0) break;    
        memset(f,0,sizeof(f));
        for(int i=1;i<=m;i++)
        {
            int x=0,y=0;
            scanf("%d%d",&x,&y);
            f[x][y]=f[y][x]=true;
        }
        ne=0,memset(note,0,sizeof(note));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i!=j&&!f[i][j]) NewEdge(i,j);
        tot=0,top=0,cnt=0;
        memset(dfn,0,sizeof(dfn));
        memset(sta,0,sizeof(sta));
        for(int i=1;i<=n;i++) dcc[i].clear();
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        memset(book,0,sizeof(book));
        for(int i=1;i<=cnt;i++) 
        {
            int len=dcc[i].size();
            for(int j=0;j<len;j++)    mark[dcc[i][j]]=true;
            flag=false,col[dcc[i][0]]=1,dfs(dcc[i][0]);
            if(flag)
                for(int j=0;j<len;j++) book[dcc[i][j]]=true;
            for(int j=0;j<len;j++)
                mark[dcc[i][j]]=false,col[dcc[i][j]]=0;
        }
        int ans=0;
        for(int i=1;i<=n;i++)
            if(!book[i]) ans++;
        printf("%d
",ans);
    }
    return 0;
}
Spoj 2878
原文地址:https://www.cnblogs.com/wozaixuexi/p/10220736.html