POJ2186 强连通分支(Strongly_Connected_Components)

这个题如果用传递闭包,显然是不行的。结点有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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值