Tarjan 强连通分量 + 解释

版权声明:并没卵用的版权声明~喜欢的就在评论区聊聊天吧~~ https://blog.csdn.net/Frocean/article/details/82821117

日常补东西 这次是Tarjan算法 原理还是别翻我这里了......但其他的肯定很详细~~

强连通分量

先给个题目 见下

题目大意:n (2 ≤ n ≤ 10000) 个点,m (2 ≤ m ≤ 50000) 条边 求大于1的强连通分量的个数好了Tarjan算法走起

关于遍历

Tarjan 算法最主要的还是两个数组

dfn 用来表示当前点的dfs序编号 (同树剖那个id)

low 则是表示当前点所在强连通分量的 ' 根 ' 就是该强连通分量的点中编号最小的那个 主要用途就是当 dfn[p] == low[p] 时说明该强连通分量搜完了 然后存下来

还有一个数组 (我个人叫他from) 用来存某点所在的强连通分量的编号 但本题用不到 这个更新...放在弹出栈的循环里

遍历的话这里用栈(stick然而我嫌名字太长用que代替) 每搜到一个点就丢进去

如何遍历呢

先把最重要的 dfn 和 low 的 值赋了 一开始都等于遍历的编号一个点的 dfn 值 等于其 low 值 的时候 说明这是一个强连通分量的根 开始的时候是一样的 就是把每个点都当成一个强连通分量 之后再去搜索 等到搜完一圈了 此时的点就可以更新到之前某处的点 回溯之后该点的上一个点 上两个点 上三个点......就都能连到一块了 这样一个强连通分量就出来了

但所有的点都要搜到哇 没搜到的点的话还要继续哇

于是用一个日常的 o 数组 判断是否找过 没找过就从那个点开始 tarjan 即可

遍历完了怎么办? 开始弹出该强连通分量的所有点 当然别忘了弹出根 建议用 do{} while() 然而我是用 while() 外面再加一次的

然后啥都没了

之后根据题目所需自己瞎搞搞就好 本题代码放下来 链接这里再放一个

#include <cstdio>
using namespace std;
const int MAXN = 10010;
const int MAXM = 100010;
struct edge {
	int to,next;
} e[MAXM];
int first[MAXN],dfn[MAXN],low[MAXN],que[MAXN],num[MAXN]/*,id[MAXN]*/;
int g,tot,t;
short o[MAXN];
int min(int x,int y) {return x < y ? x : y;}
void add(int x,int y)
{
	e[++tot].next = first[x];
	e[tot].to = y;
	first[x] = tot;
}
void tarjan(int p)
{
	dfn[p] = ++tot;
	low[p] = tot;
	que[++t] = p;
	o[p] = 1;
	int b;
	for (int a = first[p] ; a ; a = e[a].next)
	{
		b = e[a].to;
		if (!dfn[b]) tarjan(b),low[p] = min(low[p],low[b]); else
		if (o[b]) low[p] = min(low[p],dfn[b]);
	}
	if (dfn[p] == low[p])
	{
		++g;
		while (que[t] != p) /*id[que[t]] = p,*/
		++num[g],o[que[t--]] = 0;
		++num[g],o[que[t--]] = 0;
	}
}
int main()
{
	int n,m,x,y;
	scanf("%d%d",&n,&m); while (m--)
	scanf("%d%d",&x,&y),add(x,y);
	tot = 0; for (int a = 1 ; a <= n ; ++ a) if (!o[a]) tarjan(a);
	tot = 0; for (int a = 1 ; a <= g ; ++ a) if (num[a] > 1) ++tot;
	printf("%d\n",tot);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Frocean/article/details/82821117