tarjan 算法

来源:互联网 发布:网络40禁书百度 编辑:程序博客网 时间:2024/06/05 20:48
强联通: 两个顶点之间互相可达,这两个顶点称为强联通。


有向图中中,任意两个顶点间互相可达,那么这个图称为强联通图。


不是强联通图的极大强联通子图称为强联通分量。


强联通分量的分解

Tarjan 算法:


基于对图深度优先搜索,求得一个有向图的所有强联通分量




算法理解:



用一个深度优先搜索搜索,每个点有一个 pre[]数组记录的是这个点被搜到的顺序。



有一个low[] 数组记录的是 从这个点搜下去,可以到达的点中pre值最小的那个点 的pre值。

也就是说从这个点开始往下的所有点(后入栈的所有点,因为是根据深搜的顺序入的栈)都属于一个强联通分量。


每次深搜会返回的是吧,这个时候 你可以 得到 与当前点相邻的点的low 值,记录的是从相邻的点深搜下去所能达到的最小的pre 值的点的pre 值

在所有这些点和当前点的 low 值 中 取最小的low 值, 当一个点相邻的所有点都已经遍历过后,说明已经搜到底了,如果它相邻的点有还在栈中的,在所有相邻点的 pre 值 和 当前点的 low 值中取最小的那个,说明 从这个点可以到达最先被搜到的点.


有些点 从它本身开始搜,会搜到比他还要先被访问到的点,说明这个点不是这个强联通分量中最先被搜到点。


如果一个点从它开始搜,都到的最先被访问的点就是他本身,那么说明这个点就是这个强联通分量中最先被访问到的点,那么从这个点以后入栈的点都是这个这个强联通分量中的点。


主循环中 对每一个没有被访问过的点都进行深搜。



一个强联通分量中总有一个点要被先访问到 ,程序中通常是主循环的顺序决定。。



记录一个点是否还在栈中可以用一个数组,数组的值记录的是 该点所在的强联通分量的编号:




建议看一下 刘汝佳的 《算法竞赛入门经典指南》,反正我看了很久终于看懂了。网上的东西也有很多,不过总感觉挺乱的,额,个人见解,谁叫我菜呢!





#include <iostream>#include <stack>#include <string>#include <alrorithm>using namespace std;const int maxn=10001; // ----- 视程序给出的数据范围而定vector<int> G[maxn];//邻接表int pre[maxn],low[maxn],scc_cnt, dfs_cnt; //int in_stack[maxn];//这个数组表示一个点是否还在stack 中,如果还在那么说明这个点还不属于任意一个联通分量stack<int> s;void dfs(int u){pre[u]=low[u]=++dfs_cnt;s.push(u);for(int i=0;i<G[u].size();i++){    int v =G[u][i];    if(!pre[i]){        dfs(v); // 接下去深搜,返回的时候,这个点就有明确的low 值了,代表从这个点深搜下去能够达到的 最先被访问的点        low[u]=min(low[u],low[v]);    }   // pre[i]初始化设为0 ,代表这个点没有被访问过    else if(!in_stack[v]){ //如果这个点还被访问过,而且这个点还在栈中,那么这个点的pre 值很可能比 当前点的low值小。        low[u]=min(low[u],pre[v])    }}if(low[u]==pre[u]){ // 说明这个点就是一个强联通分量的最先被访问的点scc_cnt++;for(;;) {  // 从当前栈弹出这个联通分量的所有点    int x=s.top(),s.pop();   in_stack[x]= scc_cnt;   if(u==x)break;  // 直到 x==u 退出}}}int main(){dfs_cnt=scc_cnt=0;memset(pre,0,sizeof(pre));memset(low,0,sizeof(low));for(int i=1;i<=n;i++){    if(!pre[i])dfs(i);}return 0;}



0 0
原创粉丝点击