tarjan求割点模板

本文介绍了一种基于Tarjan算法的割点(割顶)检测方法。通过深度优先搜索(DFS),该算法能够有效地找出图中所有割点,并提供了一个完整的C++实现示例。特别注意的是,在实际应用过程中不应移除重边,以免减少双联通分量。

题目:【模板】割点(割顶)

 

注意:

1、第一个点要特判。

2、一开始作死写了个判重边,把重边去掉了,WA了很久……后来才发现去了重边后悔少一些双联通分量

 

代码:

#include<bits/stdc++.h>
using namespace std;

#define maxn 100000

int n,m;
vector<int> a[maxn+5];

int pre[maxn+5]={0},low[maxn+5];
int clk=0;
bool iscut[maxn+5]={0};

void readin() {
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
}

void tarjan(int x,int fa) {
	pre[x]=low[x]=++clk;
	
	int child=0;
	for(int i=0; i<a[x].size(); i++) {
		int v=a[x][i];
		if(v==fa) continue;
		
		if(!pre[v]) {
			tarjan(v,x);
			low[x]=min(low[x],low[v]);
			child++;
            if(low[v]>=pre[x]&&~fa) { 
				iscut[x]=true; 
            } 
		} else if(pre[v]<pre[x]) {
			low[x]=min(low[x],pre[v]);
		}
	}
	if(fa==-1&&child>=2) iscut[x]=true;
}

int main() {
	readin();
	for(int i=1; i<=n; i++) if(!pre[i]) tarjan(i,-1);
	int ans=0;
	for(int i=1;i<=n;i++) if(iscut[i]) ans++;
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) if(iscut[i]) printf("%d ",i);
	return 0;
}

 

### Tarjan算法的实现与解释 Tarjan算法是一种用于图论中寻找连通性信息的经典算法,可以高效地解无向图中的和桥(边)。以下是关于该算法在方面的实现和详细解释。 #### 算法核心思想 Tarjan算法基于深度优先搜索(DFS),通过维护两个数组 `dfn[]` 和 `low[]` 来记录节的访问顺序以及能够回溯到的最早祖先节。 - `dfn[u]` 表示节 `u` 在 DFS 遍历中的访问顺序。 - `low[u]` 表示从节 `u` 或其子树出发,能够回溯到的最早的祖先节的 `dfn` 值。 对于一个节 `u`,如果满足以下条件之一,则它是: 1. 如果 `u` 是根节,并且它有两个或以上的子树[^1]。 2. 如果 `u` 不是根节,并且存在某个子节 `v`,使得 `low[v] >= dfn[u]`[^3]。 #### 算法模板代码 以下是使用邻接表存储图的 Tarjan 算法模板代码: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 1e5 + 5; int dfn[MAXN], low[MAXN], index_cnt; bool is_cutpoint[MAXN]; vector<int> G[MAXN]; int n, m; void tarjan(int u, int fa) { dfn[u] = low[u] = ++index_cnt; int child = 0; // 记录子树数量 for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!dfn[v]) { // 如果 v 没有被访问过 child++; tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] >= dfn[u]) { is_cutpoint[u] = true; // 满足条件 } } else if (v != fa) { // 如果 v 已经访问过且不是父节 low[u] = min(low[u], dfn[v]); } } if (fa == -1 && child == 1) { // 根节只有一个子树时不为 is_cutpoint[u] = false; } } void find_cutpoints() { index_cnt = 0; fill(dfn, dfn + MAXN, 0); fill(is_cutpoint, is_cutpoint + MAXN, false); for (int i = 1; i <= n; i++) { if (!dfn[i]) { tarjan(i, -1); // 对每个连通分量调用一次 tarjan } } for (int i = 1; i <= n; i++) { if (is_cutpoint[i]) { cout << "Cut point: " << i << endl; } } } ``` #### 数据结构选择的重要性 在实际应用中,推荐使用邻接表而非邻接矩阵来存储图。邻接表的时间复杂度为 \(O(N + M)\),而邻接矩阵的时间复杂度至少为 \(O(N^2)\)[^2]。因此,对于大规模稀疏图,邻接表是更优的选择。 #### 算法步骤说明 1. 初始化 `dfn[]` 和 `low[]` 数组,确保所有节未被访问。 2. 对于每个未访问的节,调用 `tarjan` 函数进行 DFS 遍历。 3. 在遍历过程中,更新 `low[]` 值,并根据条件判断当前节是否为。 4. 输出所有。 #### 示例 假设有一个无向图,节数 \(n=6\),边数 \(m=7\),边集为 \((1,2), (1,3), (2,3), (2,4), (3,5), (4,6), (5,6)\)。运行上述代码后,可以找到为节 \(2\) 和 \(3\)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值