荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: kaman (Aspire追月|Fight for Final), 信区: ACMICPC
标  题: 典型算法与ACM题目解析(02)—有向图的强连通分量zz
发信站: 荔园晨风BBS站 (Wed Nov  8 15:34:42 2006), 站内

发信人: Sempr (偶尔郁闷||进了WorldFinals2007就去pie MM), 信区: Algorithm
标  题: 典型算法与ACM题目解析(02)—有向图的强连通分量
发信站: 武汉白云黄鹤站 (2006年08月17日20:17:44 星期四), 站内信件

这道题是POJ的2186题,题意是说,有一群牛,总数为N(N<=10000),题目数据给出牛之间的
关系,比如说1仰慕2,2仰慕3等等,设这种仰慕是可以传递的,如果1仰慕2,那么1也会同
时仰慕2仰慕的那些牛,如果一头牛被所有的牛都仰慕,那么它将是最受欢迎的牛,题目要
求的是有多少牛是"最受欢迎的"。

这道题目大家第一眼看到可能感觉直接模拟,但是由于数据量巨大,模拟的话肯定是过不
了的,而且题目中还会出现环路的情况,比如1=>2,2=>3,3=>1,所以这解这道题最好的方
法是使用有向图的强连通分量。

在同一个强连通分量里的所有的牛之间是互相仰慕的,我们将其缩为一点,并且只记录这
一点的牛的数量,如果有最受欢迎的牛存在,那么这个图将是连通图,并且出度为零的点
只有一个,我们可以用并查集来判是否连通,然后计算每个节点的出度,即可求出最受欢
迎的牛的数量。

求强连通分量有三种算法,分别是Kosaraju算法,Gabow算法和Tarjan算法,其中Kosaraju
算法要对原图和逆图都进行一次DFS,而另外两种算法只要DFS一次就可以了

用了Gabow算法和Kosaraju算法各写了一遍

在时间上Gabow算法是122ms,Kosaraju算法是61ms
理论上Gabow算法要比Kosaraju快些的,因为Gabow算法只对原图进行一次DFS
而Kosaraju要进行两次
Gabow反而慢的原因可能是我用了STL里的stack

PS:我没看懂Gabow算法,完全是对着书上写的

代码如下:

Kosaraju算法

/*
    求有向图的强连通分量的Kosaraju's algorithm
    By Sempr ---- 2006.06.16
*/
#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;

Edge E[G_size];
Graph GA[G_size], GT[G_size];
int N, M;
int G_end;
int order[V_size], id[V_size], vis[V_size], in[V_size];
int cnt, scnt, pos;

void Insert(int s, int e) //建立原图和逆图
{
    int p;
    p = s;
    while (GA[p].next)
        p = GA[p].next;
    GA[G_end].id = e;
    GA[p].next = G_end;

    p = e;
    while (GT[p].next)
        p = GT[p].next;
    GT[G_end].id = s;
    GT[p].next = G_end;

    G_end++;
}

void DFST(int x) //对逆图进行搜索
{
    int p, q;
    vis[x] = 1;
    p = GT[x].next;
    while (p)
    {
        q = GT[p].id;
        if (!vis[q])
            DFST(q);
        p = GT[p].next;
    }
    order[cnt++] = x;
}

void DFSA(int x) //对原图进行搜索
{
    int p, q;
    vis[x] = 1;
    id[x] = cnt;
    p = GA[x].next;
    while (p)
    {
        q = GA[p].id;
        if (!vis[q])
            DFSA(q);
        p = GA[p].next;
    }
}

void Solve() //主要过程
{
    int s, e;
    int i;

    memset(GA, 0, sizeof(GA));
    memset(GT, 0, sizeof(GT));
    memset(E, 0, sizeof(E));
    G_end = N + 1;

    for (i = 0; i < M; i++)
    {
        scanf("%d %d", &s, &e);
        E[i].s = s - 1;
        E[i].e = e - 1;
        Insert(s - 1, e - 1);
    }

    memset(vis, 0, sizeof(vis));
    cnt = 0;
    for (i = 0; i < N; i++)
    {
        if (!vis[i])
        {
            DFST(i);
        }
    }

    memset(vis, 0, sizeof(vis));
    cnt = 0;
    for (i = N - 1; i >= 0; i--)
    {
        if (!vis[order[i]])
        {
            DFSA(order[i]);
            cnt++;
        }
    }

    for (i = 0; i < M; i++)
    {
        s = id[E[i].s];
        e = id[E[i].e];
        if (s != e)
        {
            in[s]++;
        }
    }
    scnt = cnt;
    cnt = 0;
    for (i = 0; i < scnt; i++)
    {
        if (in[i] == 0)
        {
            pos = i;
            cnt++;
        }
    }
    if (cnt != 1)
    {
        printf("0\n");
    }
    else
    {
        cnt = 0;
        for (i = 0; i < N; i++)
        {
            if (in[id[i]] == pos)
            {
                cnt++;
            }
        }
        printf("%d\n", cnt);
    }
}

int main()
{
    while (EOF != scanf("%d %d", &N, &M))
        Solve();
    return 0;
}

Gabow算法
/*
    求有向图的强连通分量的Gabow's algorithm
    By Sempr ---- 2006.06.14
*/
#include <stdio.h>
#include <algorithm>
#include <stack>
#define size 11000
using namespace std;

int N, M;

typedef struct Node
{
    int id;
    int next;
} Node;

typedef struct Edge
{
    int s, e;
}Edge;

Edge E[size * 6];
Node G[size * 7];
int pre[size], id[size];
typedef stack<int> Stack;
Stack S, path;
int end;
int cnt, scnt;
int vis[size];
int in[size];

void Insert(int s, int e)
{
    int p = s;
    while (G[p].next)
    {
        p = G[p].next;
        if (G[p].id == e)
        {
            return;
        }
    }
    G[p].next = end;
    G[end].id = e;
    end++;
}

void scR(int w)
{
    int v;
    int p, t;
    pre[w] = cnt++;
    S.push(w);
    path.push(w);
    p = G[w].next;
    while (p)
    {
        t = G[p].id;
        p = G[p].next;
        if (pre[t] == -1)
            scR(t);
        else if (id[t] == -1)
            while (pre[path.top()] > pre[t])
                path.pop();
    }
    if (path.top() == w)
        path.pop();
    else
        return;
    do
    {
        id[v = S.top()] = scnt;
        S.pop();
    }
    while (v != w);
    scnt++;
}

void Gabow()
{
    int i;
    memset(pre, -1, sizeof(pre));
    memset(id, -1, sizeof(id));
    cnt = 0;
    scnt = 0;
    while (!S.empty())
        S.pop();
    while (!path.empty())
        path.pop();
    for (i = 1; i <= N; i++)
    {
        if (pre[i] == -1)
        {
            scR(i);
        }
    }
}

void DFS(int w, int d)
{
    int p = G[w].next;
    d++;
    pre[w] = 1;
    if (d > cnt)
    {
        cnt = d;
    }
    while (p)
    {
        if (pre[G[p].id] != 1)
        {
            DFS(G[p].id, d);
        }
        p = G[p].next;
    }
}

void Solve()
{
    int i, s, e;
    int pos;
    memset(G, 0, sizeof(G));
    end = N + 10;
    memset(in, 0, sizeof(in));
    for (i = 0; i < M; i++)
    {
        scanf("%d %d", &s, &e);
        E[i].s = s;
        E[i].e = e;
        Insert(s, e);
    }
    Gabow();
    memset(G, 0, sizeof(G));
    memset(pre, 0, sizeof(pre));
    end = scnt + 10;
    for (i = 0; i < M; i++)
    {
        s = id[E[i].s];
        e = id[E[i].e];
        if (s != e)
        {
            in[s]++;
        }
    }
    cnt = 0;
    for (i = 0; i < scnt; i++)
    {
        if (in[i] == 0)
        {
            pos = i;
            cnt++;
        }
    }
    if (cnt != 1)
    {
        printf("0\n");
    }
    else
    {
        cnt = 0;
        for (i = 1; i <= N; i++)
        {
            if (in[id[i]] == pos)
            {
                cnt++;
            }
        }
        printf("%d\n", cnt);
    }
}

int main()
{
    while (scanf("%d %d", &N, &M) != EOF)
        Solve();
    }
    return 0;
}

--
▁▃▄▅▃▂▁ /╲_                   .
         __  ╱/ ╲╲╱╲            ヌ  ゑ
       ╱  ╳ ;  /▁▂▃▄▃▂▁     リ  θ           一山一水一圣人
      /  \ ╲╱ \  / :    ╲────╮         ——白雲黃鶴ShanDong版
    ╱\   \╱╳╲ :: |/ ﹨: ╲──╮╰────────────╮
   / . \  /;╲     /\/; ; :﹨ ╲  ╰────────────╮╰───
※ 来源:·武汉白云黄鹤站 bbs.whnet.edu.cn·


--

     Science is what we understand well enough to explain to a computer.
     Art is everything else we do.

                                                 ———— Donald E. Knuth



※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.14.224]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店