UVALive3523 [Knights of the Round Table] tarjan求无向图双联通分量

题目链接


题意:有n个骑士经常举行圆桌会议,商讨大事。每次圆桌会议至少应有3个骑士参加,且相互憎恨的骑士不能坐在圆桌旁的相邻位置。如果发生意见分歧,则需要举手表决,因为骑士不能保证每次提议都一致通过,因此参加会议的骑士数目必须是奇数,以防止赞同和反对票一样多。知道哪些骑士相互憎恨之后,你的任务是统计有多少个骑士不可能参加任何一个会议。

solution:

以骑士为节点建图(不互相憎恨的骑士之间连边,表示可以坐在相邻位置)

原问题就可以转化为求不在任意一个奇圈(节点有奇数个的圈)的点的数量

因为二分图不含奇圈,如果一个双联通分量是二分图,则只需要找出图中的

双联通分量,在判断是否为二分图,就可以算出答案了。

ps:含有奇圈的双联通分量中的每一个点都属于至少一个奇圈。
解释一下:

这里写图片描述

U1,U2 属于同一个奇圈
因为是双联通分量,所以v与U1,U2都连通。
因为是奇圈,所以U1到U2的两条路长度为一奇一偶。
所以无论黄线长为奇数或偶数,v,U1,U2组成的圈中一定有一个奇圈。

#include <stack>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1e3 + 7;

struct Edge{ int u, v; };

stack<Edge> S;
vector<int> G[N], bcc[N];
int dfn[N], low[N], pos[N], bcc_cnt, id;
int col[N], odd[N];

void dfs(int u, int fa){
    dfn[u]=low[u]=++id;
    for ( int i=0; i<G[u].size(); i++ ){
        int v=G[u][i];
        if( v==fa ) continue;
        Edge e=(Edge) { u, v };
        if( !dfn[v] ){
            S.push(e);
            dfs(v, u);
            low[u]=min(low[u], low[v]);
            if( dfn[u]<=low[v] ){
                bcc[++bcc_cnt].clear();
                for(;;){
                    Edge x=S.top(); S.pop();
                    if( pos[x.v]!=bcc_cnt ) {pos[x.v]=bcc_cnt; bcc[bcc_cnt].push_back(x.v); }
                    if( pos[x.u]!=bcc_cnt ) {pos[x.u]=bcc_cnt; bcc[bcc_cnt].push_back(x.u); }
                    if( x.u==u && x.v==v ) break;
                }
            } 
        }else if( dfn[v]<low[u] ){
                S.push(e);
                low[u]=min(low[u],dfn[v]);
            }
    }
}

void find_bcc(int n){
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(pos,0,sizeof(pos));
    bcc_cnt=id=0;
    for ( int i=1; i<=n; i++ )
        if( !dfn[i] ) dfs(i, 0);
}


bool bipartite(int u, int b){
    for ( int i=0; i<G[u].size(); i++ ){
        int v=G[u][i];
        if( pos[v]!=b ) continue;
        if( col[v]==col[u] ) return false;
        if( !col[v] ){
            col[v]=3-col[u];
            if( !bipartite(v, b) ) return false; 
        }
    }
    return true;
}
int A[N][N];
int main(){
    int n, m;
    while( scanf("%d%d", &n, &m )!=EOF &&n ){
        for ( int i=0; i<=n; i++ ) G[i].clear();
        memset(A,0,sizeof(A));
        for ( int i=1; i<=m; i++ ){
            int x, y;
            scanf("%d%d", &x, &y );
            A[x][y]=A[y][x]=1;
        }
    //  printf("\n");
        for ( int i=1; i<=n; i++ )
          for ( int j=i+1; j<=n; j++ )
            if( !A[i][j] ) /*printf("%d %d\n", i, j),*/ G[i].push_back(j), G[j].push_back(i);
        find_bcc(n);
    //  printf("\n");
    /*  for ( int i=1; i<=bcc_cnt; i++ ){
            for ( int j=0; j<bcc[i].size(); j++ )
                printf("%d ", bcc[i][j] );
            printf("\n");
        }
    */
        memset(odd,0,sizeof(odd));
        memset(pos,0,sizeof(pos));
    //  printf("\n");
        for ( int i=1; i<=bcc_cnt; i++ ){
            memset(col,0,sizeof(col));
            for ( int j=0; j<bcc[i].size(); j++ ) pos[bcc[i][j]]=i;
    //      printf("%d\n", bcc[i].size() );

                int u=bcc[i][0];
                col[u]=1;
                if( !bipartite(u,i)){
    //              printf("%d\n", i);
                    for ( int j=0; j<bcc[i].size(); j++ ) odd[bcc[i][j]]=1;
                } 

    //      printf("\n");
        }
    //  printf("\n");
        int ans=n;
        for ( int i=1; i<=n; i++ ) if( odd[i] ) ans--;
        printf("%d\n", ans );   
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值