实用算法实现-第 17 篇 强连通分支

求强连通分量的著名算法:Kosaraju算法,Gabow算法和Tarjan算法。其中Kosaraju算法要对原图和逆图都进行一次DFS,而另外两种算法只要进行一次DFS即可。[i]文是介绍Gabow算法的论文。

17.1    Kosaraju算法

Kosaraju算法虽然要进行两次DFS,但是复杂度仍然是O(V+E),而且比较容易理解。

17.1.1   实例

PKU JudgeOnline, 2186, Popular Cows.

17.1.2   问题描述

有一群牛,总数为N。给出牛之间的M个仰慕关系,该关系可以传递,比如:1仰慕2,2仰慕3,那么1也仰慕3,如果一头牛被所有的牛都仰慕,那么它将是最受欢迎的牛,求出有多少牛是最受欢迎的。

先输入N、M,再输入M个仰慕关系。

17.1.3   输入

33

12

21

2 3

17.1.4   输出

1

17.1.5   分析

先对图求强连通分支,将所有强连通子图合并为一个结点,形成一个新图。

不难证明:在图中,如果将强连通分支看做一个结点,那么如果该结点出度不为0,则该SCC中的牛不是被所有牛仰慕的牛。反证法即可证明。

更进一步,不难证明:如果图中有牛被所有牛仰慕,那么有且只有一个出度为0的,且从图中的任何一个结点都能到达(连通性)的SCC,该SCC包含被所有牛仰慕的牛。反证法可以证明。

也可以知道:如果超过一个SCC的出度为0,那么连通性得不到保障。

可以证明:存在所有牛仰慕的牛,当且仅当出度为0的SCC有且只有一个。“仅当”很容易证明,下面证明“当”。假设只有一个SCC出度不为0时,但是却不存在所有牛仰慕的牛。那么必然意味着,有结点不能到达该SCC。因为如果所有结点都能到达该SCC,那么很自然的该SCC就是包含所有被其它所有牛仰慕的牛。在不能到达该SCC的结点中至少存在一个结点,其出度为0。因为如果所有结点的出度都不为0,那么所有结点必然形成一个环。形成环的结点在构造强连通分支的时候是要合并在一起的,矛盾。所以必然至少存在一个结点,其出度为0。假设和结果矛盾,故此,命题得证。

所以要求被所有牛仰慕的牛的个数,只需要求强连通分支,然后统计出度为0的个数。如果个数不为1则输出0。然后找到出度为0的那个SCC,。如果能就输出该SCC的牛的个数。

1.1.6   程序

#include <stdio.h>
#include <string.h>
#define G_size 100000 //边的最大个数
#define V_size 11000 //点的最大个数
 
typedef struct Graph
{
     int id;//记录了结点的序号
     int next;
}Graph;
 
typedef struct Edge
{
     int s, e;
}Edge;
 
EdgeE[G_size];
GraphGA[G_size];
GraphGT[G_size];
int N, M;
int G_end;
int order[V_size];
int id[V_size];//在逆序的时候记录了SCC的序号
int vis[V_size];//遍历图的时候使用
int in[V_size];//计算反向SCC图的入度,也就是逆向之前的SCC图出度
int cnt, scnt, pos;
 
void Insert(int s, int e) //建立原图和逆图
{
     int p;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值