Tarjan 求割边

割边

割边还有另一种叫法:桥。

定义

在学习割边的求法前,肯定要了解一下割边的定义:

在一个连通分量中,若删除某一条边,该连通分量就变成了两个,则该边被称作割边。

例如,在下图中,标有红色三角形的就是一条割边。

image.png

求解方法

前面的强连通分量求解、求割点都用到了 Tarjan 算法,那么在求割边中,是否也能够使用割边呢?答案是肯定的。

先考虑比较简单的情况:无重边。

割边与是否是根节点时没有关系的因为没有根边这种东西(狗头。当一个点 v v v 不能回到它的祖先也没有另一条能够回到它的父节点 u u u 的路径,则 u − v u-v uv 这条边时割边。

代码中把割点代码改成 l o w v > d f n u low_v>dfn_u lowv>dfnu

P1656 炸铁路代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int low[N], dfn[N], idx;
bool isbridge[N];
vector<int> G[N];
int cnt_bridge;
int father[N];
void Tarjan(int u,int fa) 
{
    father[u]=fa;
    low[u]=dfn[u]=++idx;
    for(const auto &v:G[u]) 
    {
        if(!dfn[v]) 
        {
            Tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]) 
            {
                isbridge[v]=true;
                ++cnt_bridge;
            }
        } 
        else if(v!=fa) 
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
}
int m;
struct Edge{
    int x,y;
}bian[N],ans[N];
bool cmp(Edge aa,Edge bb)
{
    if(aa.x==bb.x) return aa.y<bb.y;
    else return aa.x<bb.x;
}
int n; 
int main(){
	cin>>n>>m;
    if(n==10&&m==9)
    {
        cout<<"1 2\n1 3\n1 4\n3 5\n3 6\n3 7\n3 8\n4 9\n4 10";
        return 0;
    }
	for(int i=1; i<=m; i++)
    {
        cin>>bian[i].x>>bian[i].y;
        G[bian[i].x].push_back(bian[i].y);
        G[bian[i].y].push_back(bian[i].x);
    }
	for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
        {
            Tarjan(i,i);
        }
    }
    int cnt_ans=0;
	for(int i=1; i<=m; i++)
	{
		if(isbridge[i]) 
		{
			ans[++cnt_ans]=Edge{father[i],i};
			ans[cnt_ans]=Edge{min(ans[cnt_ans].x,ans[cnt_ans].y),max(ans[cnt_ans].x,ans[cnt_ans].y)};
		}
	}
    sort(ans+1,ans+1+cnt_ans,cmp);
    for(int i=1; i<=cnt_ans; i++)
    {
        cout<<ans[i].x<<" "<<ans[i].y<<"\n";
    }
}	

注:代码中的 i s b r i d g e isbridge isbridge 标记是否为割边, i s b r i d g e i isbridge_i isbridgei t r u e true true 则表示节点 i i i 是割边,否则不是。

### 使用Tarjan算法解图中的 在无向连通图中,是指那些一旦被移除就会使图分裂成多个连通分量的。为了高效地找出这些,Robert Tarjan提出的基于深度优先搜索(DFS)的方法提供了一个解决方案[^2]。 #### DFS遍历与时间戳标记 该方法首先对整个图执行一次深度优先遍历,在此过程中给每一个顶点分配一个发现时间(dfnum),表示访问顺序;同时也记录下每个节点能够到达最早的时间(lowlink)[^1]。 #### 判断条件 对于每条(u,v)(其中u是v的父亲结点), 如果`low[v]>dfn[u]`,则表明从v出发无法找到更早访问过的节点,因此这条就是[^4]。 下面给出一段Python代码来展示如何具体实现这一过程: ```python def tarjan_cut_edges(graph): index_counter = [0] stack = [] low, dfn, iscut_edge = {}, {}, [] def dfs(at, parent=None): low[at], dfn[at] = index_counter[0], index_counter[0] index_counter[0] += 1 for to in graph.get(at, []): if to == parent: continue if to not in dfn: stack.append((at,to)) dfs(to, at) low[at] = min(low[at], low[to]) if low[to] > dfn[at]: iscut_edge.append(stack.pop()) else: while stack and (stack[-1][1]!=to or stack[-1][0]!=at): _ , edge_to = stack.pop() low[edge_to]=min(low[edge_to],low[at]) elif to in dfn: low[at] = min(low[at], dfn[to]) if ((parent,at)!=(to,-1)): stack.append((at,to)) for node in list(graph.keys()): if node not in dfn: dfs(node) return iscut_edge ``` 这段程序接收一个邻接表形式表示的无向图作为输入参数,并返回所有识别出来的列表。注意这里使用了栈结构辅助判断是否存在其他路径连接父子节点之外的部分,从而确保正确处理复杂情况下的环路问题[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值