您的位置:首页 > 其它

POJ2186 强连通分支(Strongly_Connected_Components)

2011-03-21 12:17 267 查看
这个题如果用传递闭包,显然是不行的。结点有10000个,效率O(N*N*N)

还好有个Korasaju算法:

procedure Strongly_Connected_Components(G);

 begin

  1.深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。

2.深度优先遍历G的转置图GT,选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历,一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。

  3. 第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量

 end;

这里将同一强连通分支的结点弄成一个点,然后找出度为0的点,如果出度为0的点只有一个,那么可解,解为出度为0的强连通分支上的所有点的个数。

对于DAG图有个定理:DAG图中唯一出度为0的点,一定可以由任何点出发均可达(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点);

#include <iostream>

#include <vector>

#include <cstring>

#include <cstdio>

using namespace std;

const int MAXM=50000;

const int MAXN=10000;

int finish[MAXN+5];

vector<int> adjacent[MAXN+5];

vector<int> radjacent[MAXN+5];

bool visit[MAXN+5];

int finishtime;

int distribute[MAXN+5];

bool same[MAXN+5];

void dfs(int u)

{

int size;

int i;

visit[u]=true;

size=adjacent[u].size();

for(i=0;i<size;i++)

{

if(visit[adjacent[u][i]]==false)

{

visit[adjacent[u] [i]]=true;

dfs(adjacent[u][i]);

}

}

finishtime++;

finish[finishtime]=u;

}

void rdfs(int u,int num)

{

int i;

int size=radjacent[u].size();

visit[u]=true;

distribute[u ]=num;

for(i=0;i<size;i++)

{

if(visit[radjacent[u][i]]==false)

{

visit[radjacent[u][i]]=true;

distribute[radjacent[u][i]]=num;

rdfs(radjacent[u][i],num);

}

}

}

int main()

{

int N,M;

int i;

int j;

int from,to;

while(scanf("%d %d",&N,&M)!=EOF)

{

for(i=1;i<=M;i++)

{

scanf("%d %d",&from,&to);

adjacent[from].push_back(to);

radjacent[to].push_back(from);//图的转置

}

finishtime=0;

memset(visit,false,sizeof(visit));

for(i=1;i<=N;i++)

{

if(visit[i]==false)

{

dfs(i);

}

}//对图DFS,按结束时间进栈,先结束先进栈

memset(visit,false,sizeof(visit));

int num=1;

for(i=finishtime;i>=1;i--)

{

if(visit[finish[i]]==false)

{

rdfs(finish[i],num);

num++;

}

}//对转置的图DFS,按出栈顺序DFS

num--;

memset(same,0,sizeof(same));

for(i=1;i<=N;i++)

{

for(j=0;j<adjacent[i].size();j++)

{

if(distribute[i]!=distribute[adjacent[i][j]])

{

same[distribute[i]]=1;//标记出度不为0的块

}

}

}

int sub;

int same_sum=0;

for(i=1;i<=num;i++)

{

if(same[i]==0)

{

sub=i;

same_sum++;//统计出度为0的块的个数

}

}

int ans;

ans=0;

if(same_sum>1)

{

printf("0/n");

continue;

}//出度为0的块的数量超过1

else

{

for(i=1;i<=N;i++)

{

if(distribute[i]==sub)

{

ans++;

}

}//统计出度为0的块的节点的个数

printf("%d/n",ans);

}

}

return 0;

}

参考文献:北大2010summerschool的gw_connect.ppt
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: