POJ 2186 Popular Cows(强连通分量)

题目大意:

给定一个有向图,求有多少个顶点是由任何顶点出发都可达的。

补充1:

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

解题思路:

1、求出所有的强连通分量

2、每个强连通分量缩成一点,则形成一个有向无环图DAG

3、DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案

4、DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0

补充2:

Korasaju算法求有向图强连通分支

  begin

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

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

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

  end

算法复杂度分析(邻接表)

深度优先搜索的复杂度:O(V+E)

计算GT的复杂度:0或者O(V+E)

总的复杂度:O(V+E)

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#define M 10005
using namespace std;

struct Cow{
    int th;
    int starT;
    int endT;
    int mark;
}cow[M];

vector <int> adj[M];
vector <int> adjT[M];

int n, m, t, lable;
int vis[M];

void dfs1(int v) {   //深度优先遍历G,算出每个结点的结束时间
    vis[v] = 1;
    for(int i=0; i<adj[v].size(); i++) {
        if(vis[adj[v][i]] == 0) {
            cow[adj[v][i]].starT = t++;
            vis[adj[v][i]] = 1;
            dfs1(adj[v][i]);
            cow[adj[v][i]].endT = t++;
        }
    }
    return ;
}

void dfs2(int v) {  //深度优先遍历GT,做分类标记
    vis[v] = 1;
    for(int i=0; i<adjT[v].size(); i++) {
        if(vis[adjT[v][i]] == 0) {
            cow[adjT[v][i]].mark = lable;
            vis[adjT[v][i]] = 1;
            dfs2(adjT[v][i]);
        }
    }
}

int cmp1(Cow c1, Cow c2) {
    return c1.endT > c2.endT;
}

int main() {
    int i, j, k;
    int a, b;
    while(~scanf("%d%d", &n, &m)) {
        memset(adj, 0, sizeof(adj));
        memset(adjT, 0, sizeof(adjT));
        for(i=0; i<n; i++) {
            adj[i].clear();
            adjT[i].clear();
        }
        for(i=0; i<m; i++) {
            scanf("%d%d", &a, &b);
            a--; b--;
            adj[a].push_back(b);    //原图
            adjT[b].push_back(a);   //转置图
        }
        t = 1;
        memset(vis, 0, sizeof(vis));
        for(i=0; i<n; i++) {
            cow[i].th = i;
            if(vis[i] == 0) {
                cow[i].starT = t++;
                dfs1(i);
                cow[i].endT = t++;
            }
        }

        sort(cow, cow+n, cmp1);
        lable = 1;
        memset(vis, 0, sizeof(vis));
        for(i=0; i<n; i++) {
            if(vis[cow[i].th] == 0) {
                cow[cow[i].th].mark = lable;
                dfs2(cow[i].th);
                lable ++;
            }
        }

        int tmpA[M];
        memset(tmpA, 0, sizeof(tmpA));
        for(i=0; i<n; i++) {                  //标记缩点后的点的出度情况
            for(j=0; j<adj[cow[i].th].size(); j++) {
                if(cow[cow[i].th].mark != cow[adj[cow[i].th][j]].mark) {
                    tmpA[cow[cow[i].th].mark] = 1;
                }
            }
        }

        int ans = 0;
        int ansN = 0;   //统计缩点后的优先无环图的入度为0的点的个数
        for(i=1; i<lable; i++) {
            if(tmpA[i] == 0) {
                ans = i;
                ansN ++;
            }
        }
        if(ansN > 1) {
            printf("0\n");
            continue;
        }
        int res = 0;
        for(i=0; i<n; i++) {
            if(cow[i].mark == ans) {
                res ++;
            }
        }
        printf("%d\n", res);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值