POJ2553————The Bottom of a Graph(tarjan算法)

22 篇文章 0 订阅

The Bottom of a Graph
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 10668 Accepted: 4391

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

Source



大意:题目理解的难点在于什么是sinks,也就是比如有u,v两个点,如果u能到v,能够推出v能到u,那么u,v就是sinks。

比如第二个样例,1能到2,但是2不能到1,所以1不是sink,但是由于2谁也到不了,所以2也是sink

那么也就是相当于求所有的初度为0的强连通分量


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;

const int MAXN = 5010;
int stack[MAXN];
int top=0;
bool vis[MAXN];
bool used[MAXN];
int dfn[MAXN];
int low[MAXN];
int ComNum = 0;
int indexx=0;
vector <int> G[MAXN];
vector <int> Com[MAXN];
int InCom[MAXN];
int out_dgree[MAXN];
vector <int> res;


void init(int n){
    top=0;
    memset(vis,0,sizeof(vis));
    memset(used,0,sizeof(used));
    fill(dfn,dfn+MAXN,-1);
    fill(low,low+MAXN,-1);
    ComNum=0;
    indexx=0;
    for(int i=1;i<=n;i++){
        G[i].clear();
        Com[i].clear();
    }
    memset(InCom,0,sizeof(InCom));
    memset(out_dgree,0,sizeof(out_dgree));
    res.clear();
}

void tarjan(int i){
    dfn[i]=low[i]=++indexx;
    vis[i]=true;
    used[i]=true;
    stack[++top]=i;
    for(int k=0;k<G[i].size();k++){
        int v=G[i][k];
        if(dfn[v]==-1){
            tarjan(v);
            low[i]=min(low[i],low[v]);
        }
        else if(vis[v]){
            low[i]=min(low[i],dfn[v]);
        }
    }
    int j;
    if(dfn[i]==low[i]){
        ComNum++;
        do{
            j=stack[top--];
            vis[j]=false;
            Com[ComNum].push_back(j);
            InCom[j]=ComNum;
        }
        while(j!=i);
    }
}

int main()
{
    int n,m;
    while(scanf("%d",&n)){
        if(n==0)
            break;
        scanf("%d",&m);
        init(n);
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
        }
        for(int i=1;i<=n;i++){
            if(!used[i])
                tarjan(i);
        }
        for(int u=1;u<=n;u++){
            for(int j=0;j<G[u].size();j++){
                int v=G[u][j];
                if(InCom[u]!=InCom[v]){
                    out_dgree[InCom[u]]++;
                }
            }
        }
        for(int i=1;i<=ComNum;i++){
            if(out_dgree[i]==0){
                for(int j=0;j<Com[i].size();j++){
                    res.push_back(Com[i][j]);
                }
            }
        }
        sort(res.begin(),res.end());
        for(int i=0;i<res.size();i++){
            if(i==res.size()-1)
                printf("%d",res[i]);
            else
                printf("%d ",res[i]);
        }
        printf("\n");
    }
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值