zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    【ESXI6.0】 ESXI6.0安装时无法安装网卡驱动的解决方法及将网卡驱动加载进ISO
    [转]Vs解决方案的目录结构设置和管理
    在控制台编译运行java程序详细指导
    详解Linux安装GCC方法
    MySQL server has gone away 问题的解决方法
    eclipse或Myeclipse中web项目没有run on server时怎么办?
    DTM/DEM/DSM/DOM/DLG
    linux常用命令
    为什么很多地方看到初始值是1970年8月1日
    mongoDB
  • 原文地址:https://www.cnblogs.com/tztqwq/p/13812616.html
Copyright © 2011-2022 走看看