算法随笔——tarjan

随便记点
链接

有向图求强连通分量(SCC)

SCC 定义:任意两个点都可以互相到达的子图。
一张图中每个节点只属于一个 SCC。

dfn 时间戳,low 追溯值,表示 subtree(X) 能到达的点的最小时间戳且点在栈中(标准意义上来说是只经过一条边,实际没有影响)。

dfs 过程中维护一个栈,表示当前可能凑成一个 SCC 的元素。

具体过程

  1. dfs,x 被第一次访问,low[x]=dfn[x],入栈
  2. 扫描出边 (x,y),
    • y 没被访问,low[x]=min(low[x],low[y]) ——根据定义可知。
    • y 访问过且于栈内(即不属于任何一个SCC,low[x]=min(low[x],dfn[y])
  3. 扫描完后,若有 low[x]=dfn[x],说明子树出现横叉边,与 x 构成了环。则栈顶一直到 x 之间的元素为一个 SCC

缩点

可以将每一个 SCC 看作一个点,则新建出一个有向无环图 (DAG)。
image
值得注意的是建 DAG 过程中有重边并不会影响拓扑排序。

重要结论!!!

代码中 SCC 的编号递减顺序即为 DAG 的拓扑序。
因此有的时候跑完 tarjan 并不需要在跑一次 topo
非常的牛。

code

void tarjan(int u)
{
	dfn[u] = low[u] = ++tot;
	sta.push(u);ins[u] = 1; //标记是否在栈中
	for (auto j : v[u])
	{
		if (!dfn[j]) tarjan(j),low[u] = min(low[u],low[j]);
		else if (ins[j]) low[u] = min(low[u],dfn[j]);
	}	
	if (low[u] == dfn[u])
	{
		cnt ++;
		int cur;
		do
		{
			cur = sta.top();
			scc[cnt].push_back(cur),sta.pop();ins[cur] = 0;
			id[cur] = cnt;
		}while (cur != u);
	}
}

for (int i = 1;i <= n;i++) if (!dfn[i]) tarjan(i); //图可能不联通
for (int i = 1;i <= n;i++)
	for (auto j : v[i])
		if (c[i] != c[j]) v2[c[i]].push_back(c[j]);

无向图求联通分量

边双连通分量

相当于把图上的桥去掉。
判定方法:

posted @   codwarm  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示