无向图的双连通分量 Knights of the Round Table POJ - 2942

点双连通分量即子图中没有割点

const int maxn=1e3+5;
struct edge
{
    int u,v;
};
int pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt;
vector <int> g[maxn],bcc[maxn];//bcc存储每个双连通分量的点
stack<edge> s;
int dfs(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;
    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;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                while(1)
                {
                    edge x=s.top();
                    s.pop();
                    if(bccno[x.u]!=bcc_cnt)//x.u没有添加过
                    {
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u]=bcc_cnt;
                    }
                    if(bccno[x.v]!=bcc_cnt)//x.v没有添加过
                    {
                        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;
    return lowu;
}
void find_bcc(int n)
{
    memset(pre,0,sizeof(pre));
    memset(iscut,0,sizeof(iscut));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=0;
    for(int i=1; i<=n; i++)//注意顶点序号是从0还是从1开始
    {
        if(!pre[i])
            dfs(i,-1);
    }
}

题目链接:https://vjudge.net/problem/POJ-2942
题意:有n个人,m条边表示憎恨关系,其中关系是双向的,现在有一个会议,所有人围成一个圈。其中有憎恨关系的不能相邻,并且最终参加会议人数为奇数,求有多少人不能参加任何一场会议。
思路:参考https://blog.csdn.net/lyy289065406/article/details/6756821。两个定理:1.简单圈上的节点属于同一个连通分量 2.如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。 先把图中的双连通分量求出来,然后挨个判断每个双连通分量是否是一个二分图即可。能够参加会议的人数就是所有不是二分图的双连通分量的人数和。注意可能会有重复的人。

#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <stack>
using namespace std;
const int maxn=1e3+5;
struct edge
{
    int u,v;
};
int pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt;
vector <int> g[maxn],bcc[maxn];
stack<edge> s;
int dfs(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;
    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;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                while(1)
                {
                    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;
    return lowu;
}
void find_bcc(int n)
{
    memset(pre,0,sizeof(pre));
    memset(iscut,0,sizeof(iscut));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=0;
    for(int i=0; i<n; i++)
    {
        if(!pre[i])
            dfs(i,-1);
    }
}
int a[maxn][maxn],odd[maxn],color[maxn];
bool judge(int u,int b)//判断二分图
{
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(bccno[v]!=b)
            continue;
        if(color[v]==color[u])
            return false;
        if(!color[v])
        {
            color[v]=3-color[u];
            if(!judge(v,b))
                return false;
        }
    }
    return true;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m))
    {
        if(n==0&&n==m)
            break;
        for(int i=0; i<n; i++)
        {
            g[i].clear();
        }
        memset(a,0,sizeof(a));
        for(int i=0; i<m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            u--,v--;
            a[u][v]=a[v][u]=1;
        }
        for(int i=0; i<n; i++)
        {
            for(int j=i+1; j<n; j++)
            {
                if(!a[i][j])
                {
                    g[i].push_back(j);
                    g[j].push_back(i);
                }
            }
        }
        find_bcc(n);
        memset(odd,0,sizeof(odd));
        for(int i=1; i<=bcc_cnt; i++)
        {
            memset(color,0,sizeof(color));
            for(int j=0; j<bcc[i].size(); j++)
            {
                bccno[bcc[i][j]]=i;
            }
            int u=bcc[i][0];
            color[u]=1;
            if(!judge(u,i))
            {
                for(int j=0; j<bcc[i].size(); j++)
                {
                    odd[bcc[i][j]]=1;
                }
            }
        }
        int ans=n;
        for(int i=0; i<n; i++)
            if(odd[i])
                ans--;
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要获取无向图双连通分量,可以使用Tarjan算法。以下是一个Python实现的示例代码: ``` def tarjan_biconnected_components(graph): """ Tarjan算法计算无向图双连通分量 :param graph: 无向图,用邻接列表表示 :return: 双连通分量列表 """ index_counter = [0] stack = [] lowlink = {} index = {} result = [] bridges = [] def strongconnect(node): # 为节点赋予唯一的索引 index[node] = index_counter[0] lowlink[node] = index_counter[0] index_counter[0] += 1 stack.append(node) # 对于每个相邻节点v for v in graph[node]: # 如果v没有被访问过,则递归调用strongconnect if v not in index: strongconnect(v) lowlink[node] = min(lowlink[node], lowlink[v]) # 如果v是一个桥,则将桥添加到bridges列表中 if lowlink[v] == index[v]: bridges.append((node, v)) # 如果v已经在堆栈中,则更新此节点的lowlink elif v in stack: lowlink[node] = min(lowlink[node], index[v]) # 如果节点是一个连接分量的根,则弹出堆栈,并收集连通分量 if lowlink[node] == index[node]: connected_component = [] while True: v = stack.pop() connected_component.append(v) if v == node: break result.append(connected_component) for node in graph: if node not in index: strongconnect(node) return result ``` 使用示例: ``` graph = { 1: {2, 3}, 2: {1, 3, 4}, 3: {1, 2, 4}, 4: {2, 3, 5}, 5: {4} } result = tarjan_biconnected_components(graph) print(result) # 输出:[[1, 2, 3], [4, 5]] ``` 以上代码实现了Tarjan算法,用于计算无向图双连通分量。传入的图以邻接列表表示,返回的结果是双连通分量的列表。对于结果中的每个双连通分量,其包含的节点组成了一个强连通分量,即任意两个节点都有一条路径相连。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值