Tarjan求割点与桥

2024-02-04 14:38
文章标签 tarjan 求割点

本文主要是介绍Tarjan求割点与桥,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

割点与桥的概念

割点:

简单的说就是,删掉这个点和这个点有关的边,图就不是连通图,分裂成了多个不连接的子图。

桥:

删掉这条边后,图就不再是连通图,分裂成为多个不连通的子图。

求割点

算法思想:

首先选定一个根节点,从根节点开始遍历整个图(使用dfs)

  • 对于根节点:
    如果有2棵即以上的子树,就是割点(因为如果去掉这个点,这两棵子树就不能互相到达)
  • 对于非根节点
    如果节点U的所有孩子节点可以不通过父节点U而访问到U的祖先节点,那么说明:即使去掉节点U也不影响图的连通性,U不是割点。
    如果节点U至少存在一个孩子节点,必须通过父节点U才能访问到U的祖先节点,那么去掉节点U之后,顶点U的祖先节点和孩子节点就不连通了,此时U是一个割点。

维护数组

对于求割点的Tarjan算法数组中,其意义大体类似于求连通分量,但是略有不同

  • dfn[ ]:依然表示顶点u被首次搜到的序号
  • low[u]:表示顶点u及其子树中的点,通过非父子边(回边),能够绕到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。这里不再是最早的祖先了,因为是无向图,所以并没有意义。
  • cut[u]:表示u是否为割点

low[u]的计算

  • 若当前顶点为u,则low[u]=dfn[u],即最早只能回溯到自身。
  • 对于边(u,v):
    如果v未访问过,继续DFS,DFS完之后,low[u] =min(low[u],low[v]);
    如果v访问过(且u不是v的父亲),就不需要继续DFS了,因为一定有dfn[v]<dfn[u] , low[u] = min(low[u],dfn[v])。

关于为什么是low[u]=min(low[u],dfn[v])而不能是low[u]=min(low[u],low[v])
(ps:这个是本人在学习时最为不解的地方)

  • 在求强连通分量时,其实即使换掉也没有问题,这是因为如果v已经在栈中,那么说明u,v一定在同一个强连通分量中,所以到最后low[u]=low[v]是必然的,提前更新也不会有什么问题
  • 但是在求割点时,由于是无向图,所以我们在存图的时候一个边存了两便,而我们必须要绕开连接u及其父节点的边,此时如果v恰好是u的父节点(u,v之间有边相连)且只有这么一条路径,那么我们更换后无法绕开那条边,所以就会出现问题。

在这里插入图片描述
上面数组的标号即为dfs序,当你用两种方法去模拟时,并观察2是否为割点时,你就能比较清楚的感觉到这里为什么要这样。

代码模板

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+20;
int head[N],edge[N<<1],Next[N<<1],tot;
int dfn[N],low[N],n,m,num,root,ans;
bool cut[N];
void add_edge(int a,int b)
{edge[++tot]=b;Next[tot]=head[a];head[a]=tot;
}
void Tarjan(int x)
{dfn[x]=low[x]=++num;int flag=0;for(int i=head[x];i;i=Next[i]){int y=edge[i];if(!dfn[y]){Tarjan(y);low[x]=min(low[x],low[y]);if(low[y]>=dfn[x]){flag++;if(x!=root||flag>1)cut[x]=true;}}else low[x]=min(low[x],dfn[y]);}
}
int main()
{scanf("%d%d",&n,&m);memset(cut,false,sizeof(cut));for(int i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);add_edge(a,b);add_edge(b,a);}for(int i=1;i<=n;i++)if(!dfn[i]) {root=i;Tarjan(i);}for(int i=1;i<=n;i++)if(cut[i]) ans++;printf("%d\n",ans);for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i);//system("pause");return 0;
}

求桥

桥的求法其实类似的,它的求法可以看成是割点的一种特殊情况,当结点u的子结点v的后代通过反向边只能连回v,那么删除这条边(u,v)就可以使得图G非联通了。用Tarjan算法里面的时间戳表示这个条件,就是low[v]>dfn[u]

代码模板

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+20;
int head[N],edge[N<<1],Next[N<<1],tot;
int dfn[N],low[N],n,m,num;
bool bridge[N<<1];
void add_edge(int a,int b)
{edge[++tot]=b;Next[tot]=head[a];head[a]=tot;
}
void Tarjan(int x,int Edge)
{dfn[x]=low[x]=++num;for(int i=head[x];i;i=Next[i]){int y=edge[i];if(!dfn[y]){Tarjan(y,i);low[x]=min(low[x],low[y]);if(low[y]>dfn[x])bridge[i]=bridge[i^1]=true;}else if(i!=(Edge^1))low[x]=min(low[x],dfn[y]);}
}
int main()
{scanf("%d%d",&n,&m);tot=1;for(int i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);add_edge(a,b);add_edge(b,a);}for(int i=1;i<=n;i++)if(!dfn[i]) Tarjan(i,0);for(int i=2;i<=tot;i=i+2)if(bridge[i])printf("%d %d\n",edge[i^1],edge[i]);//system("pause");return 0;
}

这篇关于Tarjan求割点与桥的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677839

相关文章

【tarjan缩点+小拓展】【POJ-2186】

用刚搞到的模板,去过了一道题 差不多是个模板题 有一群奶牛,奶牛A可以关注B,关注具有传递性,给出奶牛之间的关注关系,问有几只奶牛得到了所有其他奶牛的关注? 互相关注的可以看成一个点,所以直接tarjan算法 + 缩点 新图中,出度为0的点只能有一个,因为如果有两个,这两个新点(原连通分量)就一定是互相没有关注联系的 然后答案就是这个出度为0 的新点(原连通分量)中包含的原来点的个

【hh大神的】Tarjan + 缩点 模板

此模板来自 notonlysuccess 原文链接: http://www.notonlysuccess.com/index.php/tarjan/ 大神就是吊啊。 不多说了,想学tarjan ,资料网上是有一堆的 怎么说。。我现在理解的还不算太透彻,但用模板总行吧 这里只存一下模板 使用时注意清空和存图! (基于个人习惯,稍微有些小改动) #define

Tarjan的脱机最小公共祖先算法详解

Tarjan的脱机最小公共祖先算法详解 一、算法概述二、算法伪代码三、C语言实现四、证明与分析 在解决脱机最小公共祖先(Least Common Ancestors, LCA)问题时,Tarjan算法提供了一种高效的途径。该算法通过一次深度优先搜索(DFS)遍历整棵树,并利用并查集(Union-Find)数据结构来维护节点之间的祖先关系,从而找到任意两个节点的最小公共祖先。

poj 3352 Road Construction(边连通+tarjan+缩点)

http://poj.org/problem?id=3352 题意:简化一下原题题意,意思就是给定一个连通图,问至少要加入几条边使得整个图变成一个边连通图,即图中任意两点都有两条以上的路径(不一定直接相连)。 思路:tarjan算法,设置一个low数组,在建立深搜树的过程中,我们会得到每个节点的low值,对于low值相等的节点在同一个双连通分量中。由于在同一个边连通分量中的点的“地

poj 2942 Knights of the Round Table(双连通分量+tarjan+二分图判定)

http://poj.org/problem?id=2942 题意: 有N个骑士,给出某些骑士之间的仇恨关系,骑士们开会时会围坐在一个圆桌旁。一次会议能够顺利举行,要满足两个条件: 1:任意相互憎恨的两个骑士不能相邻 2:开会人数为大于2的奇数 若某个骑士任何会议都不能参加,那么就必须将他踢出,给出骑士之间的仇恨关系,问最少需要踢出多少个骑士? 思路: 题目要求踢出的人最少,那

poj 2186 Popular Cows(tarjan + 强连通分量 + 缩点)

http://poj.org/problem?id=2186 题意:有n头牛,m个膜拜关系,膜拜关系是不可逆的而且是单向传递的,比如A膜拜B,B膜拜C,那么A也膜拜C,但B不一定膜拜A。最后问有多少头牛满足条件:除了它自己,其他所有的牛都膜拜它。 思路: 问题可以抽象为:给定一个有向图,n个顶点,m条有向边,有多少个顶点满足:其他所有的点都能到达该点。 首先假如图G是一个有向树

poj 3694 Network(tarjan + LCA)

http://poj.org/problem?id=3694 题意:对于一个无向连通图,问加入某条边后,图中有桥的数目。 思路: 根据tarjan算法求出初始图的桥的数目,并用数组bridge标记桥的终点,在tarjan深搜树中求出每个节点的父节点(数组father表示)以及它们的深度,用于以后迭代求LCA。 因为加入某条边后,树中就会存在环,而环中的每条边都不再是桥,这就与求LCA

强连通Tarjan算法入门

A - 迷宫城堡 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit  Status Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称

强连通分量的tarjan算法

一、数据集形式 其中:1595446(节点个数) 0(起始边) 1(终边) 二、数据集 数据集下载 三、实现代码 // Tarjan.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "time.h"#include <fstream>#includ

SCC Tarjan算法

int dfs_clock,scc_cnt;int low[maxn],dfn[maxn],sccno[maxn]; //sccno[i]为i所在的SCC编号stack<int>S;vector<int>map[maxn];void tarjan( int u, int fa ){low[u] = dfn[u] = ++dfs_clock;S.push(u);for( int