UVA-1364.Knights of the Round Table 无向图BCC

题目链接:https://vjudge.net/problem/UVA-1364

题意:有n个人参加会议,互相憎恨的人不能坐在相邻的位置,并且每个会议参加的人数必须是奇数,求有多少个人不能参加任何一个会议。

思路:如果两个人可以坐在一起,则在他们之间建立一条无向边。求不在任何一个简单奇圈上面的点的个数。简单圈上面的点必然属于同一个点双联通分量,因此首先需要找出所有的点双联通分量、因为二分图是没有奇圈的,所以需要求那些不是二分图的点双联通分量。虽然这些点双联通分量一定含有奇圈,那么是否是所有的点都在奇圈上面呢。v属于点双联通分量B,但是不在属于B的奇圈C上面。根据点双联通的性质,v一定可以到达C中的一个结点u1,v也一定可以到达C中的入一个结点u2,在C中u1到u2的两条路的长度一奇一偶,总能构建出一个奇圈。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#include<vector>
using namespace std;
const int MAXN=1e3+100,INF=0x3f3f3f3f,MOD=1e9+7;
int n,m;
int vis[MAXN][MAXN];
vector<int>G[MAXN];
int dfs_color=0;    ///dfs时间戳
int pre[MAXN],post[MAXN];
int bcc_cnt=0;   ///联通分量
int low[MAXN];  ///u及其后代所能连回的最早祖先的pre值
int iscut[MAXN];    ///割点
vector<pair<int,int> >birdge;   ///
struct edge
{
    int u,v;
};
stack<edge>S;
int bccno[MAXN];    ///点所在的双联通分量
vector<int>bcc[MAXN];   ///双联通分量
int dfs(int u,int fa)
{
    int lowu=pre[u]=++dfs_color;
    int child=0;
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        edge e=(edge)
        {
            u,v
        };
        if(!pre[v])
        {
            S.push(e);
            child++;
            int lowv=dfs(v,u);
            lowu=min(lowu,lowv);
            if(lowv>=pre[u])
            {
                iscut[u]=true;
                if(lowv>pre[u]) birdge.push_back(make_pair(u,v));
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                while(!S.empty())
                {
                    edge x=S.top();
                    S.pop();
                    if(bccno[x.u]!=bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u]=bcc_cnt;
                    }
                    if(bccno[x.v]!=bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v]=bcc_cnt;
                    }
                    if(x.u==u&&x.v==v) break;
                }
            }
        }
        else if(pre[v]<pre[u]&&v!=fa)
        {
            S.push(e);
            lowu=min(lowu,pre[v]);
        }
    }
    if(fa<0&&child==1) iscut[u]=0;
    low[u]=lowu;
    return low[u];
}
void find_bcc()
{
    bcc_cnt=0;
    dfs_color=0;
    memset(pre,0,sizeof(pre));
    memset(iscut,0,sizeof(iscut));
    memset(bccno,0,sizeof(bccno));
    for(int i=1; i<=n; i++)
        if(!pre[i]) dfs(i,-1);
}
void init(int n,int m)
{
    memset(vis,0,sizeof(vis));
    for(int i=0; i<=n; i++) G[i].clear();
    birdge.clear();
    while(m--)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        vis[u][v]=vis[v][u]=1;
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<i; j++)
        {
            if(vis[i][j]) continue;
            G[i].push_back(j);
            G[j].push_back(i);
        }
    }
}
int color[MAXN];
int odd[MAXN];
bool bipartite(int u,int d)
{
    for (int i = 0; i < G[u].size(); i++)
    {

        int v=G[u][i];
        if (bccno[v]!=d) continue;
        if (color[v]==color[u]) return false;
        if (!color[v])
        {
            color[v]=3-color[u];
            if (!bipartite(v,d)) return false;
        }
    }
    return true;
}
int solve()
{
    memset(odd,0,sizeof(odd));
    for(int i=1; i<=bcc_cnt; i++)
    {
        for(int j=0;j<bcc[i].size();j++) bccno[bcc[i][j]]=i;
        memset(color,0,sizeof(color));
        color[bcc[i][0]]=1;
        if(!bipartite(bcc[i][0],i))
        {
            for (int j=0; j<bcc[i].size(); j++)
                odd[bcc[i][j]]=1;
        }
    }
    int ans=0;
    for(int i=1; i<=n; i++)
        if(!odd[i]) ans++;
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        init(n,m);
        find_bcc();
        cout<<solve()<<endl;
    }
    return 0;
}
无向图BCC+二部图

 

转载于:https://www.cnblogs.com/GeekZRF/p/6640275.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值