POJ 2553 The Bottom of a Graph 缩点之后求出度为0的强联通分量的元素


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

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


此题的题意关键在这句话: A node v in a graph G=(V,E) is called a sink, if for every node w in G that is reachable from v, v is also reachable from w.
如果v能到达的所有点w,并且w也能够到达v,那么称v是一个sink。
要求出所有的sink,那么如果是在一个强联通分量里面的话,那么肯定v能够到达的点w,w也能够到达v,但是如果缩点之后,这个强联通分量(设为a)连接其它某个分量(设为b),那么这个a强联通分量里面的点都能够连接b强联通分量里面的某个点,而b里面的这个点却不能连接a里面的点,所以不满足条件。
因此只要将这个图缩点之后,找出度为0的强联通分量,则此分量里面的所有元素即为所求。
//688K	79MS
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 5007
using namespace std;
int cnt,scnt,n,m,num,begin;
int dfn[M],low[M],head[M],vis[M],stack[M],belong[M],out[M];
struct E
{
    int v,to;
}edg[M*1000];
void init()
{
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(belong));
    memset(out,0,sizeof(out));
    memset(stack,0,sizeof(stack));
    cnt=scnt=num=begin=0;
}
void addedge(int a,int b)
{
    edg[num].v=b;edg[num].to=head[a];
    head[a]=num++;
}
void tarjan(int x)
{
    int v;
    low[x]=dfn[x]=++cnt;
    stack[++begin]=x;
    for(int i=head[x];i!=-1;i=edg[i].to)
    {
        v=edg[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(!vis[v])
            low[x]=min(low[x],dfn[v]);
    }
    if(low[x]==dfn[x])
    {
        scnt++;
        do
        {
            v=stack[begin--];
            belong[v]=scnt;
            vis[v]=1;
        }while(v!=x);
    }
}
int main()
{
    while(scanf("%d",&n),n)
    {
        scanf("%d",&m);
        init();
        int a,b;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a,b);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])tarjan(i);
        for(int i=1;i<=n;i++)
            for(int j=head[i];j!=-1;j=edg[j].to)
            {
                int v=edg[j].v;
                if(belong[i]!=belong[v])//如果不在同一个连通分量里面
                    out[belong[i]]++;//出度++
            }
        bool flag=true;
        for(int i=1;i<=n;i++)
            if(!out[belong[i]])//如果出度是0的强联通分量,则输出此强联通分量的元素
            {
                if(flag){printf("%d",i);flag=false;}
                else printf(" %d",i);
            }
        printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值