首页 > 编程语言 >Tarjan算法

Tarjan算法

时间:2023-06-05 22:35:12浏览次数:48  
标签:Tarjan int 算法 dfn maxn low define

Tarjan算法

1 算法简介

还记得无向图判连通块吗?对于无向图中,判连通块是一件很容易的事。你只需要dfs(深度优先搜索)一下就可以了。但是,如果我们把无向图换成有向图呢?

这就是另一个故事了......

2 算法定义

Robert Tarjan计算机科学家,以LCA,Tarjan等算法闻名。Tarjan是求强连通分量的一个强力的算法。

要理解Tarjan这个算法,我们先讲几个定义:

强连通分量 :

对于一个分量,若任意两个点相通,则称为强连通分量。

树边 :

对于一个图的dfs树,它的树边便是此图的树边。

返祖边 :

对于一个图的dfs树,可以使得儿子节点返回到它的祖先的边为返祖边。

横插边 :

对于一个图的dfs树,可以使得一个节点到达另一个节点且它们互不是祖先的边为横插边。

3 算法详细

先讲一下我们要用到的数组。

  • dfn:第\(i\)个节点他的时间戳

  • low:第\(i\)个节点他最多经过一条返祖边所能到达的最小时间戳

  • st:一个栈,用来储存当前还未确定但已经扩展过的点

  • co:第\(i\)个节点他所在的强连通分量编号

我们讲一下算法流程。

  1. 此时来到了点 \(u\)

  2. 扩展他的子节点,这里假设现在到的子节点为 \(v\),扩展完了就来到第 \(5\) 步

  3. 三种情况:

    • !dfn[v],即还未扩展的点,则我们直接来到第 \(4\) 步
    • !co[v] && dfn[v],即还未出栈但已扩展过的点(就是返祖/横叉了嘛),我们更新一下 \(low_u = \min(low_u, dfn_v)\),并返回第 \(2\) 步。(看不懂的感性理解一下)
    • co[v] && dfn[v],即已出栈且遍历过,那么我们直接返回第 \(2\) 步
  4. 我们先对 \(v\) 这个顶点扩展一下(即返回第 \(1\) 步),然后把 \(low_u = \min(low_u, low_v)\) 更新一下,接着回到第 \(2\) 步

  5. 如果 \(dfn_u = low_u\),这说明 \(u\) 没有返祖边,它拥有属于自己的一棵子树,而此时栈中的点又一定能到 \(u\) (要不然就被弹掉了),所以此时我们只需要弹栈就可以了!

  6. 我们已经完成了所有操作,可以回溯到第 \(1\) 步了

4 模板代码

先放一道模板题 : CF427C Checkposts

这题是一道赤裸裸的强连通分量,晾一下代码。

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

# define ll long long
# define lf double
# define GO(i,a,b) for(int i = a; i <= b; i ++)
# define RO(i,b,a) for(int i = b; i >= a; i --)
# define FO(i,u,head,e) for(int i = head[u]; i; i = e[i].next)
# define CI const int
# define pii pair<int,int>
# define MP(a,b) make_pair(a,b)
# define PB(x) push_back(x)
# define mem(a,x) memset(a, x, sizeof a)
# define F first
# define S second

CI maxn = 1e5 + 7;
CI maxm = 3e5 + 7;
CI mod = 1e9 + 7;

int n, m;

struct Edge{
	int u, v, next;
}e[maxm];

int head[maxn], cnt;

void add(int u, int v){
	e[++ cnt].u = u;
	e[cnt].v = v;
	e[cnt].next = head[u];
	head[u] = cnt;
}

int dfn[maxn];
int low[maxn];
int vis[maxn]; // 0未访问, 1在栈中, 2已访问 
int tar[maxn]; // 连通分量 
int col;

int tmp;

stack <int> q;

void Tarjan(int u){
	q.push(u);
	vis[u] = 1; // 在栈中 
	low[u] = dfn[u] = ++ tmp;
	FO (i, u, head, e){
		int v = e[i].v;
		if (vis[v] == 2)
			continue;
		if (dfn[v])
			low[u] = min <int> (low[u], dfn[v]);
		else{
			Tarjan(v);
			low[u] = min <int> (low[u], low[v]);
		}
	}
	if (dfn[u] == low[u]){
		int v = q.top();
		q.pop();
		col ++;
		while (!q.empty() && v != u){
			vis[v] = 2;
			tar[v] = col;
			v = q.top();
			q.pop();
			
		}
		vis[u] = 2;
		tar[u] = col;
	}
}

vector <int> poi[maxn];

ll a[maxn];

int main(){
	cin >> n;
	GO (i, 1, n)
		scanf("%lld", &a[i]);
	cin >> m;
	GO (i, 1, m){
		int u, v;
		scanf("%d %d", &u, &v);
		add(u, v);
	}
	
	GO (i, 1, n)
		if (!vis[i])
			Tarjan(i);
	
	GO (i, 1, n)
		poi[tar[i]].PB(i);
	
	ll ans1 = 1, ans2 = 0;
	GO (i, 1, col){
		ll minn = 2e18;
		ll res = 0;
		for (int j : poi[i])
			minn = min <ll> (minn, a[j]);
		for (int j : poi[i])
			if (a[j] == minn)
				res ++;
		ans2 += minn;
		ans1 = (ans1 * res) % mod;
	} 
	
	printf("%lld %lld", ans2, ans1);
	return 0;
}

标签:Tarjan,int,算法,dfn,maxn,low,define
From: https://www.cnblogs.com/yh2021shx/p/17459114.html

相关文章

  • m基于遗传优化的凸松弛算法完成从二维人体图像中提取三维姿态的matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:   2.算法涉及理论知识概要      三维姿态估计是计算机视觉领域中一个非常重要的问题,它在许多应用中都具有重要的作用,如人机交互、姿态识别、动作捕捉等。在过去的几年中,随着深度学习技术的发展,基于深度学习的方法取得了......
  • m基于遗传优化的凸松弛算法完成从二维人体图像中提取三维姿态的matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:2.算法涉及理论知识概要三维姿态估计是计算机视觉领域中一个非常重要的问题,它在许多应用中都具有重要的作用,如人机交互、姿态识别、动作捕捉等。在过去的几年中,随着深度学习技术的发展,基于深度学习的方法取得了很大的进展,但是这些方法仍......
  • 判断对象是否存活的算法
    需要回收的位置如下 其实垃圾回收是jvm自带的功能,所以有了如下的优缺点优点:1.项目开发的时候不需要开发人员考虑内存管理2.可以有效的防止内存泄漏,更加高效的利用可使用的内存3.因为垃圾回收不再有了作用于的概念缺点:因为不了解所以使用过程中会出现内存溢出和内存泄漏的......
  • 文心一言 VS 讯飞星火 VS chatgpt (32)-- 算法导论5.2 4题
    四、利用指示器随机变量来解如下的帽子核对问题(hat-heckproblem):n位顾客,他们每个人给餐厅核对帽子的服务生一顶帽子。服务生以随机顺序将帽子归还给顾客。请问拿到自己帽子的客户的期望数是多少?文心一言:我们可以将这个问题转化为求解在指示器随机变量基础上的期望值。已知......
  • 文心一言 VS 讯飞星火 VS chatgpt (32)-- 算法导论5.2 4题
    四、利用指示器随机变量来解如下的帽子核对问题(hat-heckproblem):n位顾客,他们每个人给餐厅核对帽子的服务生一顶帽子。服务生以随机顺序将帽子归还给顾客。请问拿到自己帽子的客户的期望数是多少?文心一言:我们可以将这个问题转化为求解在指示器随机变量基础上的期望值。已知有n位......
  • 算法——字符串(一)
    1、两数相加1classSolution{2publicListNodeaddTwoNumbers(ListNodel1,ListNodel2){3ListNodepre=newListNode();4ListNodecur=pre;5intcarry=0;6while(l1!=null||l2!=null||carry!=0){7......
  • 数据结构--Dijkstra算法最清楚的讲解
    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止###基本思想通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。此外,引进两个集合S和U。S的......
  • 树之深度优先遍历算法详解(DFS实现) LeetCode94
           本文以如下树结构为例深度优先(DeepFirstSearch)       树的孩子称作子树,对于一个树进行深度优先遍历,即将其某一子树下所有节点遍历完再去遍历其他子树。遍历的顺序以根为参照可分为先序遍历,中序遍历,后序遍历。遍历方式描述先序遍历根左右中序遍历左根右后......
  • Hash表算法
    第一部分:TopK算法详解问题描述百度面试题:   搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。   假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,......
  • K-Means算法--聚类算法
    在数据挖掘中,K-Means算法是一种clusteranalysis的算法,其主要是来计算数据聚集的算法,主要通过不断地取离种子点最近均值的算法。问题K-Means算法主要解决的问题如下图所示。我们可以看到,在图的左边有一些点,我们用肉眼可以看出来有四个点群,但是我们怎么通过计算机程序找出这几个点群......