【连通图|强连通+缩点】POJ-2553 The Bottom of a Graph

19 篇文章 0 订阅
14 篇文章 0 订阅

The Bottom of a Graph
Time Limit: 3000MS Memory Limit: 65536K
   

Description

We will use the following (standard) definitions from graph theory. Let  V be a nonempty and finite set, its elements being called vertices (or nodes). Let  E be a subset of the Cartesian product  V×V, its elements being called edges. Then  G=(V,E) is called a directed graph. 
Let  n be a positive integer, and let  p=(e1,...,en) be a sequence of length  n of edges  ei∈E such that  ei=(vi,vi+1) for a sequence of vertices  (v1,...,vn+1). Then  p is called a path from vertex  v1 to vertex  vn+1 in  G and we say that  vn+1 is reachable from  v1, writing (v1→vn+1)
Here are some new definitions. A node  v in a graph  G=(V,E) is called a sink, if for every node  w in  G that is reachable from  vv is also reachable from  w. The bottom of a graph is the subset of all nodes that are sinks, i.e.,  bottom(G)={v∈V|∀w∈V:(v→w)⇒(w→v)}. You have to calculate the bottom of certain graphs.

Input

The input contains several test cases, each of which corresponds to a directed graph  G. Each test case starts with an integer number  v, denoting the number of vertices of  G=(V,E), where the vertices will be identified by the integer numbers in the set  V={1,...,v}. You may assume that  1<=v<=5000. That is followed by a non-negative integer  e and, thereafter,  e pairs of vertex identifiers  v1,w1,...,ve,we with the meaning that  (vi,wi)∈E. There are no edges other than specified by these pairs. The last test case is followed by a zero.

Output

For each test case output the bottom of the specified graph on a single line. To this end, print the numbers of all nodes that are sinks in sorted order separated by a single space character. If the bottom is empty, print an empty line.

Sample Input

3 3
1 3 2 3 3 1
2 1
1 2
0

Sample Output

1 3
2
————————————————————————————————————————————————————————————————————————————————————————————————
题意:给出一个图,求出图上所有”自己可达的顶点都能回到自己“的点。
思路:对于一个强连通图来说,所有点两两可达,因此强连通图全部都是要求的点。
如果不是强连通图:那么强连通分量和强连通分量之间的关系呢?
缩点之后,所有出度不为0的点意味着该点可达另一点但是另一点不可达该点。对于出度为0的点,因为它不可达另外的点,所以它是满足”自己可达的顶点都能回到自己“这一性质的。所以该点所代表的强连通分量的所有点都是要求的点。
P.S. 注意!因为标号是从0开始的,然而后来的缩点却是从1开始的,因此初始化的时候for循环从0到n肯定是不对的。WA了一晚上竟然都没有发现。要适当修改模板了
代码如下:
/*
 * ID: j.sure.1
 * PROG:
 * LANG: C++
 */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <climits>
#include <iostream>
#define Mem(f, x) memset(f, x, sizeof(f))
#define PB push_back
#define LL long long
using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
/****************************************/
const int N = 5555, M = 55555;
int n, m;
struct Edge {
    int v, next;
    Edge(){}
    Edge(int _v, int _next):
        v(_v), next(_next){}
}e[M];
int tot, deep, scc_cnt;
int out[N], dfn[N], scc_id[N], line[M][2], head[N];
stack <int> s;

void __init__()
{
    Mem(head, -1);
    Mem(out, 0);
    Mem(dfn, 0);
    Mem(scc_id, 0);
    tot = deep = scc_cnt = 0;
}

void add(int u, int v)
{
    e[tot] = Edge(v, head[u]);
    head[u] = tot++;
}

int dfs(int u)
{
    int lowu = dfn[u] = ++deep;
    s.push(u);
    for(int i = head[u]; ~i; i = e[i].next) {
        int v = e[i].v;
        if(!dfn[v]) {
            int lowv = dfs(v);
            lowu = min(lowu, lowv);
        }
        else if(!scc_id[v]) {
            lowu = min(lowu, dfn[v]);
        }
    }
    if(lowu == dfn[u]) {
        scc_cnt++;
        while(1) {
            int x = s.top(); s.pop();
            scc_id[x] = scc_cnt;
            if(x == u) break;
        }
    }
    return lowu;
}

int main()
{
#ifdef J_Sure
    freopen("000.in", "r", stdin);
    //freopen("999.out", "w", stdout);
#endif
    while(scanf("%d", &n), n) {
        scanf("%d", &m);
        int u, v;
        __init__();
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &u, &v);
            u--; v--;
            add(u, v);
            line[i][0] = u; line[i][1] = v;
        }
        for(int i = 0; i < n; i++) {
            if(!dfn[i]) dfs(i);
        }
        for(int i = 0; i < m; i++) {
            int u = scc_id[line[i][0]], v = scc_id[line[i][1]];
            if(u != v) out[u]++;
        }
        bool fir = false;
        for(int i = 0; i < n; i++) {
            if(!out[scc_id[i]]) {
                if(fir) printf(" ");
                printf("%d", i+1); fir = true;
            }
        }
        puts("");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值