现在的位置: 首页 > 综合 > 正文

【综合图论】PKU-2942-Knights of the Round Table

2019年09月24日 ⁄ 综合 ⁄ 共 1815字 ⁄ 字号 评论关闭

综合性很强的一道图论题,用到了补图,双连通分量,二分图等几个知识点,这里有某位大牛的神解题报告点击打开链接

题目

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
#define N 1005
vector<int> vec[N],lin[N];
stack<int> sta;
int n,step,cnt,ans,low[N],dfn[N],ins[N],col[N];
bool p[N][N];
void tarjan(int now,int pre)
{
    low[now]=dfn[now]=step++;
    sta.push(now);
    ins[now]=1;
    int len=vec[now].size();
    for(int i=0;i<len;i++)
    {
        int u=vec[now][i];
        if(u==pre)continue;
        if(dfn[u]==-1)
        {
            tarjan(u,now);
            low[now]=min(low[now],low[u]);
            if(low[u]>=dfn[now])
            {
                while(1)
                {
                    int x=sta.top();
                    sta.pop();
                    ins[x]=0;
                    lin[cnt].push_back(x);
                    if(u==x)break;
                }
                lin[cnt++].push_back(now);
            }
        }
        else
        {
            if(ins[u])low[now]=min(low[now],dfn[u]);
        }
    }
}
void dfs(int id,int u,int c)
{
    col[u]=c;
    int len=lin[id].size();
    for(int i=0;i<len;i++)
    {
        int x=lin[id][i];
        if(u==x||col[x]!=-1)continue;
        if(!p[u][x])dfs(id,x,c^1);            //找相邻的点染与当前点不同的色
    }
}
void slove()
{
    cnt=step=0;
    while(!sta.empty())sta.pop();
    memset(ins,0,sizeof(ins));
    memset(dfn,-1,sizeof(dfn));
    for(int i=0;i<n;i++)            //tarjan算法求连通分支
    {
        if(dfn[i]!=-1)continue;
        tarjan(i,-1);
    }
    bool ok[N];
    memset(ok,false,sizeof(ok));
    for(int i=0;i<cnt;i++)
    {
        memset(col,-1,sizeof(col));
        dfs(i,lin[i][0],0);            //染色
        int len=lin[i].size();
        bool flag=false;
        for(int j=0;j<len;j++)
        {
            for(int k=0;k<len;k++)
            {
                int x=lin[i][j];
                int y=lin[i][k];
                if(x!=y&&!p[x][y]&&col[x]==col[y])            //判断是否是二分图,如果x和y相邻并且他们同色就说明这个连通分支不是二分图
                {
                    flag=true;
                    break;
                }
            }
            if(flag)break;
        }
        if(flag)
        {
            for(int j=0;j<len;j++)ok[lin[i][j]]=true;            //不是二分图既是奇圈,里面的点都符合要求
        }
    }
    ans=0;
    for(int i=0;i<n;i++)
    {
        if(!ok[i])ans++;            //找出不在奇圈的点数
    }
}
int main()
{
    //freopen("a.txt","r",stdin);
    int m;
    while(scanf("%d%d",&n,&m)&&n+m)
    {
        memset(p,false,sizeof(p));
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x--,y--;
            p[x][y]=p[y][x]=true;
        }
        for(int i=0;i<n;i++)
        {
            vec[i].clear();
            lin[i].clear();
        }
        for(int i=0;i<n;i++)            //取补图
        {
            for(int j=0;j<n;j++)
            {
                if(p[i][j]&&i!=j)continue;
                vec[i].push_back(j);
            }
        }
        slove();
        printf("%d\n",ans);
    }
    return 0;
}


抱歉!评论已关闭.