Tarjan算法详解
【功能】
【算法思想】
v入栈(这里的栈不是dfs的递归时系统弄出来的栈)扫描一遍v所能直接达到的顶点k,如果k没有被访问过那么先dfs遍历k,low[v]=min(low[v],low[k]);如果k在栈里,那么low[v]=min(low[v],dfn[k])(就是这里使得low[v]不一定最小,但不会影响到这里的low[v]会小于dfn[v])。扫描完所有的k以后,如果low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。
【大概的证明】
2 .dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v](这里不再详细说),所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。
【时间复杂度】
下面附上HDU1269关于此算法的模板题的代码。原题链接点击打开链接
#include <iostream>
#include <stack>
#include <string.h>
#include <vector>
using namespace std;
int loop[10001],dfn[10001],low[10001],count,flag,time,n,m;
vector<int>w[10005];
void init()
{
int i;
for(i=0;i<=10004;i++)
{
w[i].clear();
}
}
int min(int a,int b)
{
return a<b?a:b;
}
void tarjin(int a)
{
int i;
loop[a]=1;
low[a]=dfn[a]=time++;
for(i=0;i<w[a].size();i++)
{
if(!loop[w[a][i]])
{
tarjin(w[a][i]);
low[a]=min(low[a],low[w[a][i]]);
}
if(loop[w[a][i]])
{
low[a]=min(low[a],dfn[w[a][i]]);
}
if(low[a]==dfn[a])
count++;
}
if(count>1)
flag=1;
}
int main()
{
int i,j,a,b;
while(cin>>n>>m)
{
init();
memset(loop,0,sizeof(loop));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
time=1;
flag=0;
count=0;
if(n==0&&m==0)
break;
for(i=1;i<=m;i++)
{
cin>>a>>b;
w[a].push_back(b);
}
for(i=1;i<=n;i++)
{
if(!loop[i])
tarjin(i);
}
if(!flag)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
下面的代码可以具体输出强连通的点:
#include <iostream>
#include <stack>
#include <string.h>
#include <vector>
using namespace std;
int loop[10001],dfn[10001],low[10001],count,flag,time,n,m,sign[10001];
vector<int>w[10005];
stack<int>test;
void init()
{
int i;
for(i=0;i<=10004;i++)
{
w[i].clear();
}
}
int min(int a,int b)
{
return a<b?a:b;
}
void tarjin(int a)
{
int i;
test.push(a);
loop[a]=1;
low[a]=dfn[a]=time++;
for(i=0;i<w[a].size();i++)
{
if(!loop[w[a][i]])
{
tarjin(w[a][i]);
low[a]=min(low[a],low[w[a][i]]);
}
if(loop[w[a][i]]&&!sign[w[a][i]])
{
low[a]=min(low[a],dfn[w[a][i]]);
}
if(low[a]==dfn[a])
{
count++;
}
}
if(low[a]==dfn[a])
{
int v;
if(!test.empty())
v=test.top();
while(1)
{
cout<<v;
sign[v]=1;
test.pop();
if(a==v)
break;
if(!test.empty())
v=test.top();
}
cout<<endl;
}
if(count>1)
flag=1;
}
int main()
{
int i,j,a,b;
while(cin>>n>>m)
{
init();
memset(loop,0,sizeof(loop));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(sign,0,sizeof(sign));
time=1;
flag=0;
count=0;
if(n==0&&m==0)
break;
for(i=1;i<=m;i++)
{
cin>>a>>b;
w[a].push_back(b);
}
for(i=1;i<=n;i++)
{
if(!loop[i])
tarjin(i);
}
if(!flag)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}