对于“关于有重边图的双连通分量”引发的思考

红字部分内容转载自:https://blog.csdn.net/gyarenas/article/details/8712338 博主允许转载,故以此博客为基础浅谈自己对于重边的理解。

何为重边?

顾名思义,对于两个顶点A和B,有不止一条边从A连接到B。

在学习生成树的时候我们知道,树本身不能构成回路,那么对于A到B与A到B这一对重边,对于我们求解其实是没有影响的。

也就是说:有向图的的情况比较简单只有一种强连通,重边和连向自己的边对于强连通都没有任何影响。

然而我们考虑重边往往是对于无向图,这也是最容易出错的地方。

无向图的双连通要分点双连通(biconnected)和边双连通(edge_biconnected)。

点双连通:删除一个点后仍然连通

边双连通:删除一个边后仍然连通

可以脑补一下图形,就能想到:连向自己的边对于俩种双连通也没有任何影响。

接下来这一句话就是思考的主要内容:

但是重边对点双连通没有影响,但是对于边双连通有影响。

为什么重边对点双连通没有影响,因为在删除一个点之后,与它相连的点都会被删除,那么既然会被删除,即使再多重边对它也没有太大的影响。

而重边对于边双连通的影响很大,我举个例子:

因为重边的出现,图的连通性发生了改变,然而这种改变恰巧是颠覆性的!

比如说现在我问的是图中的双连通分支的数量:第一个图是0,第二个图是1.

在求边双连通时,要求对于任意俩点至少存在两条“边不重复”的路径,所以这个时候表示图我们不能用vector了,而是用邻接表,添加边的时候我们要一次添加正反俩条边,而且要相互可以索引查找,类似网络流里的反向弧,这样在我们dfs求割边时要以边的下标作为标记,在访问一了一条边时,要把这条边和其反向边同时标记为访问,最后对所有的边进行遍历,发现low[e.v] < pre[e.u]时,同样要把正反俩条边标记成割边,最后在不经过桥的情况下dfs求出边双连通分量即可

可我们为什么要判断重边呢?

题目中给定边,既然是重边,所以对边不加判断的运算不就好了。

直接对边不加判断的运算?

但直接运算的话,我们可不好知道对于两个点究竟什么时候是单边,什么时候是多边。

而对于两个点之间的单通道,如果我们从一个点指向另一个点,而结果是它所指向的那个点是它自己,就要考虑是否有重边,如果有重边代表当前是双连通分支,我们没必要跳出当前层,反之,我们需要跳出当前层。

 

1:使用map处理重边:

map<int,int>mapit;
inline bool isHash(int u,int v)
{
    if(mapit[u*maxn+v]) return true;
    if(mapit[v*maxn+u]) return true;
    mapit[u*maxn+v]=mapit[v*maxn+u]=true;
    return false;
}

2:使用贪心处理重边:


struct node{
    int u,v;
}edge1[maxn];

bool cmp(node a,node b)
{
    if(a.u==b.u) return a.v<b.v;;
    return a.v<b.u;
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;++i)
    {
        scanf("%d%d",&u,&v);
        if(u==v) continue;
        if(u>v) swap(u,v);
        edge1[i].u=u;
        edge1[i].v=v;
    }
    sort(edge1,edge1+n,cmp);
    for(int i=0;i<n;++i)
    {
        if(i==0||&&edge1[i].u==edge1[i+1].u||edge1[i].v==edge1[i+1].v)
        {
            if(i<n-1&&edge1[i].u!=edge1[i+1].u&&edge1[i].v!=edge1[i+1].v)
            {
                addedge(edge1[i].u,edge1[i].v,true);
                addedge(edge1[i].v,edge1[i].u,true);                
            }
            else
            {
                addedge(edge1[i].u,edge1[i].v,false);
                addedge(edge1[i].v,edge1[i].u,false);                                
            }
        }
    }
}
/*
之所以进行这部分操作,是区别开哪部分是重边,哪一部分不是重边。
就用Tarjan算法求桥为例,如果当前操作的边是重边,我们不需要判断是当前点所指向的点是否又指向了子节点,反之需要判断。
*/

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值