Kosaraju 强连通分量算法

对于 "从每个点出发, 将其能到达的点标记为一个强连通分量" 这个算法, 其实搞出来的是原图缩点之后的一条链, Kosaraju 算法就是利用这些链并基于 "原图所有有向边取反后, 强连通分量依然不变, 缩点后所有边取反" 的性质, 以一个十分妙的姿势使用上方加粗算法求出所有强连通分量。(具体地, 按照原图的缩点之后的拓扑序遍历, 可以避免很多问题

算法的正确性证明并不难, 在此不述。

Kosaraju 强连通分量算法的时空效率基本都被 Tarjan 强连通分量算法吊打(这两种算法的时间和空间复杂度是完全一样的, 只有常数的差别), 但其代码编写难度是很低的, 且算法容易记住, 算是可以用来应急。(这算法根本不用刻意记啊

至于原图、反图和缩点图如何和谐共存, 其实很简单的, 要么用 vector, 要么用结构体存图, 要么用邻接表, 把边的数量开成所有图的总边数, 然后把 head[maxN] 数组改成 head[图数][maxN], 然后把 ad(u,v,w) 函数改成 ad(图编号,u,v,w) , 然后把遍历代码 for(int i=hd[x],y;i;i=nt[i]) if(y=vr[i]...) 改成 for(int i=hd[图编号][x],y;i;i=nt[i]) if(y=vr[i]...), 就可以很好地管理多个图了, 具体原理可以参考下 我的博客

以下简单地用 Kosaraju 算法实现了 LuoguP3387缩点

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+15;
const int M = 1e5+15;

int n, m, val[N], val2[N];

int ct, hd[3][N], nt[M*6], vr[M*6];
void ad(int id,int a,int b) {
	vr[++ct]=b, nt[ct]=hd[id][a], hd[id][a]=ct;
}

int deg[N];

int sccno[N], scccnt;
int s[N], tp, vis[N];

void dfs1(int x) {
	vis[x] = 1;
	for(int i=hd[1][x],y;i;i=nt[i]) if(!vis[y=vr[i]]) dfs1(y);
		s[++tp] = x;
}

void dfs2(int x) {
	sccno[x] = scccnt;
	for(int i=hd[0][x],y;i;i=nt[i]) if(!sccno[y=vr[i]]) dfs2(y);
}

void kosaraju() {
	scccnt = 0;
	for(int i=1;i<=n;++i)
		if(!vis[i]) dfs1(i);
	for(int i=n;i>=1;--i)
		if(!sccno[s[i]]) {
			++scccnt;
			dfs2(s[i]);
		}
}

int f[N];
void topo() {
    int q[N] = {0};
	for(int i=1;i<=scccnt;++i) if(!deg[i]) f[q[++q[0]]=i] = val2[i];
	for(int h=1;h<=q[0];++h) {
		int x=q[h];
		for(int i=hd[2][x];i;i=nt[i]) {
			int y=vr[i];
			f[y] = max(f[y], f[x]+val2[y]);
			if(--deg[y] == 0) q[++q[0]] = y;
		}
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;++i) scanf("%d", &val[i]);
	for(int i=0,x,y;i<m;++i) {
		scanf("%d%d",&x,&y); ad(1,x,y); ad(0,y,x);
	}
	kosaraju();
	for(int x=1;x<=n;++x) {
		val2[sccno[x]] += val[x];
		for(int j=hd[1][x];j;j=nt[j]) {
			int y=vr[j];
			if(sccno[x] != sccno[y]) ad(2,sccno[x],sccno[y]), ++deg[sccno[y]];
		}
	}
	
	topo();
	int ans = 0;
	for(int i=1;i<=scccnt;++i) ans=max(ans, f[i]);
	cout << ans;
	return 0;
}
posted @   xwmwr  阅读(195)  评论(2编辑  收藏  举报
编辑推荐:
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
· 用 C# 插值字符串处理器写一个 sscanf
点击右上角即可分享
微信分享提示