您的位置:首页 > 其它

Kosaraju求强连通分量,缩点

2018-03-11 19:37 211 查看

作用

只要两边dfs,相比于用tarjan求强连通分量,kosaraju算法还可以顺便求出强连通分量的拓扑序,有利于之后的运算.

实现方法

首先以任意一点开始dfs,并记录下搜索的后序遍历,之后将所有边反向,每次从没有搜过的点中选择后序遍历最大的搜索,此次搜到的点均与该点属于同一个强连通分量,直到所有点都被搜到过停止,可以发现越先求出的强连通分量,拓扑序越小.

例题(poj 2186)

题意

给出一副有向图,问有几个点是所有点都可以走到的.

做法

不难发现如果点i符合题意,那么有且仅有点i所在的强连通分量符合题意,还可以发现如果答案非零,则点i所在的强连通分量的拓扑序最大,因此可以用kosaraju算法求出拓扑序最大的,再检验是不是符合题意.

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 10010
#define M 50010
using namespace std;

int n,m,first
,bb,a[M],b[M],tim
,tt,lt,ans,last,an;
bool vis
;
struct Bn
{
int to,next;
} bn[M];

inline void add(int u,int v)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
first[u]=bb;
}

void dfs(int now)
{
vis[now]=1;
int p,q;
for(p=first[now]; p!=-1; p=bn[p].next)

4000
{
if(!vis[bn[p].to])
dfs(bn[p].to);
}
tim[++tt]=now;
}

void Dfs(int now)
{
ans++;
vis[now]=1;
int p,q;
for(p=first[now]; p!=-1; p=bn[p].next)
{
if(vis[bn[p].to]) continue;
Dfs(bn[p].to);
}
}

void df(int now)
{
an++;
vis[now]=1;
int p,q;
for(p=first[now];p!=-1;p=bn[p].next)
{
if(!vis[bn[p].to])
df(bn[p].to);
}
}

int main()
{
memset(first,-1,sizeof(first));
int i,j,p,q;
cin>>n>>m;
for(i=1; i<=m; i++)
{
scanf("%d%d",&p,&q);
add(p,q);
a[i]=p,b[i]=q;
}
for(i=1;i<=n;i++)
{
if(!vis[i])
dfs(i);
}
memset(first,-1,sizeof(first));
bb=0;
for(i=1; i<=m; i++)
{
add(b[i],a[i]);
}
memset(vis,0,sizeof(vis));
for(i=n;i>=1;i--)
{
if(vis[tim[i]]) continue;
ans=0;
Dfs(tim[i]);
last=tim[i];
}
memset(vis,0,sizeof(vis));
df(last);
if(an!=n)
{
puts("0");
return 0;
}
cout<<ans;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: