TARJAN算法
我先介绍一下这个算法是来干啥的:
它的用处是求强连通分量。 那么,强连通分量又是啥??
在一个有向图中,
强连通:如果两个顶点可以相互通达,则称两个顶点 强连通(strongly connected)
如果有向图G的每两个顶点都 强连通,称G是一个强连通图。非 强连通图有向图的极大强连通 子图,称为强连通分量(strongly connected components)。
算法思路:
第一点,对象图不一定是一个连通图。所以,为了判断每个点是否连通,我们引入一个时间戳。如果他==0则深搜搜他!!
我们还要定义一个变量:low[ ]:该子树中,且仍在栈中的最小时间戳,像是确立了一个关系,low[ ]相等的点在同一强连通分量中。
然后对于搜到的点寻找与其有边相连的点,判断这些点是否已经被搜索过,若没有,则进行搜索。若该点已经入栈,说明形成了环,.则更新low
在不断深搜的过程中如果没有路可走了(出边遍历完了),那么就进行回溯,回溯时不断比较low[ ],去最小的low值。如果dfn[x]==low[x]则x可以看作是某一强连通分量子树的根,也说明找到了一个强连通分量,然后对栈进行弹出操作,直到x被弹出。
算法模板:
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
int x,y,n,id,t,flag,test;
int dfn[N],head[N],block[N],low[N];
struct Node{
int v,next;
}e[N*N];
int max(int A,int B) {return A>B? A:B;}
int min(int A,int B) {return A>B? B:A;}
void add(int u,int v) {e[id].v=v;e[id].next=head[u];head[u]=id++;}
#define to e[i].v
void tj(int u)
{
dfn[u]=low[u]=++t;
for (int i=head[u];i;i=e[i].next)
if (!dfn[to])
{
tj(to);
low[u]=min(low[u],low[to]);
if (low[to]>=dfn[u]) ++block[u];
}
else low[u]=min(low[u],dfn[to]);
}
int main(){
while(1){
scanf("%d",&x);
if (x==0) break;
memset(e,0,sizeof(e));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(head,0,sizeof(head));
id=1;t=flag=n=0;
scanf("%d",&y);add(x,y);add(y,x);n=max(n,x);n=max(n,y);
whille(1){
int u,v;
scanf("%d",&u);
if (u==0) break;
scanf("%d",&v);
add(u,v);add(v,u);
n=max(n,u);n=max(n,v);
}
for (int i=1;i<=n;i++) block[i]=1;block[1]=0;
tj(1);
printf("Network #%d
",++test);
for(int i=1;i<=n;i++)
if(block[i]>1){
printf(" SPF node %d leaves %d subnets
",i,block[i]);
flag=1;
}
if(!flag)printf(" No SPF nodes
");
printf("
");
}
return 0;
}
感谢https://blog.csdn.net/yklcy_1334/article/details/54563832!!!