Popular Cows(POJ2186)(强连通+缩点)(Korasaju)

题目:http://acm.hust.edu.cn/vjudge/problem/16578

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

题解:
1、有向无环图中唯一出度为0的点,一定可以由任何点出发均可达。
2、则现在的要求就变成了把图构造成有向无环图DAG,并计算无环图中每个点的出度。
若要构建有向无环图,就要把所有的环都缩成一个点,当把所有的环都缩成一个点以后,有向无环图就出现了。
3、那要如何找到所有的环呢。我们可以来看一下强连通的定义:图论中,强连通图指每一个顶点皆可以经由该图上的边抵达其他的每一个点的有向图。那对图求一下强连通分量(可以参考满天飞的模板,我这里用的是kuangbin的Korasaju算法模板),每一个强连通分量不就是一个环么,把所有强连通分量缩成一个环,DAG就出现了。
4、如何缩点。开一个Belong数组,把所有属于同一个强连通分量的点标记成一个数字,这样就类似于缩点了。
5、计算出度方法

int de[maxn];
memset (de, 0, sizeof(de));
for (int i = 1; i <= m; i++) {
    if (Belong[e[i].l] != Belong[e[i].r]) {
        de[Belong[e[i].l]]++;
    }
}

思考:
1、有向无环图中唯一出度为0的点,一定可以由任何点出发均可达
2、因为要构造有向无环图DAG,则把每个强连通分量缩成一点
3、缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>

using namespace std;

const int maxm = 50010;
const int maxn = 10010;

struct Edge{
    int l, r;
}e[maxm];

struct Node{
    int to, next;
}edge1[maxm], edge2[maxm];

int head1[maxn], head2[maxn];
bool mark1[maxn], mark2[maxn];
int tot1, tot2;
int cnt1, cnt2;
int st[maxn];
int Belong[maxn];
int num;
int SetNum[maxn];

void addedge(int u, int v)
{
    edge1[tot1].to = v;
    edge1[tot1].next = head1[u];
    head1[u] = tot1++;
    edge2[tot2].to = u;
    edge2[tot2].next = head2[v];
    head2[v] = tot2++;
}

void DFS1(int u)
{
    mark1[u] = true;
    for (int i = head1[u]; i != -1 ;i  = edge1[i].next) {
        if (!mark1[edge1[i].to]) DFS1( (edge1[i].to));
    }
    st[cnt1++] = u;
}

void DFS2(int u)
{
    mark2[u] = true;
    num++;
    Belong[u] = cnt2;
    for (int i = head2[u]; i != -1; i = edge2[i].next)
        if (!mark2[edge2[i].to]) DFS2 (edge2[i].to);
}

void solve(int n)
{
    memset (mark1 ,false, sizeof(mark1));
    memset (mark2, false, sizeof(mark2));
    cnt1 = cnt2 = 1;
    for (int i = 1; i <= n; i++) {
        if (!mark1[i]) DFS1 (i);
    }
    for (int i = cnt1 - 1; i >= 1; i--) {
        if (!mark2[st[i]]) {
            num = 0;
            DFS2 (st[i]);
            SetNum[cnt2++] = num;
        }
    }
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen ("in.txt", "r", stdin);
    #endif // ONLINE_JUDGE
    int n, m;
    scanf ("%d%d", &n, &m);
    tot1 = tot2 = 1;
    for(int i = 1; i <= n; i++)//初始化
        {
            head1[i] = head2[i] = -1;
            mark1[i] = mark2[i] = 0;
        }
    for (int i = 1; i <= m; i++) {
        int a, b;
        scanf ("%d%d", &a, &b);
        e[i].l = a;
        e[i].r = b;
        addedge (a, b);
    }
    solve (n);
    //计算出度
    int de[maxn];
    memset (de, 0, sizeof(de));
    for (int i = 1; i <= m; i++) {
        if (Belong[e[i].l] != Belong[e[i].r]) {
            de[Belong[e[i].l]]++;
        }
    }
    int cnt = 0, res;
    for (int i = 1; i < cnt2; i++) {
        if (!de[i]) {
            cnt++;
            res = i;
        }
    }
    if (cnt > 1) printf ("0\n");
    else printf ("%d\n", SetNum[res]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值