【POJ 2553】The Bottom of a Graph 题解(Tarjan算法求有向图的强连通分量)-CSDN博客

50 阅读2分钟

我们将使用图论中的以下(标准)定义。设V是一个非空的有限集合,其元素称为顶点(或节点)。设E是笛卡尔积V×V的子集,其元素称为边。则G=(V,E)称为有向图。
设n为正整数,设p=(e1,…,en)为边ei∈E的长度为n的序列,使得顶点序列(v1,…,vn+1)的ei=(vi,vi+1)。那么p称为G中从顶点v1到顶点vn+1的路径,我们说vn+1可从v1到达,写为(v1→vn+1)。
这里有一些新的定义。图G=(v,E)中的节点v称为汇,如果对于G中从v可到达的每个节点w,v也可从w到达。图的底部是所有汇节点的子集,即,底部(G)={v∈v|∀w∈v:(v→w)中⇒(宽)→v) }。你必须计算某些图的底部。

输入
输入包含多个测试用例,每个测试用例对应于一个有向图G。每个测试用例以一个整数v开始,表示G=(v,E)的顶点数,其中顶点将由集合v={1,…,v}中的整数标识。您可以假设1<=v<=5000。其后是非负整数e,其后是e对顶点标识符v1,w1,。。。,ve,我们的意思是(vi,wi)∈E。除了这些对指定的边之外,没有其他边。最后一个测试用例后面是一个零。
输出
对于每个测试用例,在单行上输出指定图形的底部。为此,按由单个空格字符分隔的排序顺序打印所有接收节点的编号。如果底部为空,则打印空行。

Sample
Input
3 3
1 3 2 3 3 1
2 1
1 2
0
Outpu
1 3
2

思路

Tarjan算法求有向图的强连通分量,记录点所属的强连通分量序号。遍历每个点的邻接点,若该点和其邻接点所属的强连通分量序号不同,则将该点所属的强连通分量的出度加一。再次遍历每个点,若该点所属的强连通分量的出度为0,则该点为sink点。

AC代码

#include <iostream>
#include <cstring>
#include <algorithm> 
#include <stack>
#define AUTHOR "HEX9CF"
using namespace std;

const int maxn = 100005;

int cnt = 0;
struct Snode {
    int to;
    int next;
}edge[maxn];
int head[maxn];

// tarjan
int num = 0;
int dfn[maxn], low[maxn];
bool ins[maxn];
stack<int> s;
int scc;
int belong[maxn];
int outDeg[maxn];

void add(int u, int v)
{
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void print(int n)
{
    for (int j = 1; j <= n; j++)
    {
        cout << j << "-";
        for (int i = head[j]; ~i; i = edge[i].next)
        {
            cout << edge[i].to;
        }
        cout << endl;
    }
}

void init(){
    num = 0;
    cnt = 0;
    scc = 0;
    memset(head, -1, sizeof(head));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(belong, -1, sizeof(belong));
    memset(outDeg, 0, sizeof(outDeg));
}

void tarjan(int u) {
    dfn[u] = low[u] = ++num;
    ins[u] = true;
    s.push(u);
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (ins[v])
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
    // SCC
    if(low[u] == dfn[u]) {
        int v;
        do{
            v = s.top();
            s.pop();
            // cout << v << " ";
            belong[v] = scc;
            ins[v] = false;
        }while(v != u);
        scc++;
        // cout << endl;
    }
}

int main() {
    int n, e;
    while(cin >> n){
        if(!n){
            break;
        }
        init();
        cin >> e;
        for (int i = 0; i < e; i++)
        {
            int u, v;
            cin >> u >> v;
            add(u, v);
        }
        // print(n);
        for (int i = 1; i <= n; i++)
        {
            if (!dfn[i])
            {
                tarjan(i);
            }
        }
        for (int u = 1; u <= n; u++)
        {
            for (int i = head[u]; ~i; i = edge[i].next)
            {
                int v = edge[i].to;
                if (belong[u] != belong[v])
                {
                    outDeg[belong[u]]++;
                }
            }
        }
        int flg = 0;
        for (int u = 1; u <= n; u++)
        {
            if (!outDeg[belong[u]])
            {
                if (flg)
                {
                    cout << " ";
                }
                else
                {
                    flg = 1;
                }
                cout << u;
            }
        }
        cout << endl;
    }

    return 0;
}