zoukankan      html  css  js  c++  java
  • poj 2553 The Bottom of a Graph【强连通分量求汇点个数】

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

    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 Gand 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


    定义:点v是汇点须满足 --- 对图中任意点u,若v可以到达u则必有u到v的路径;若v不可以到达u,则u到v的路径可有可无。
    题意:在n个点m条边的有向图里面,问有多少个点是汇点。
    分析:首先若SCC里面有一个点不是汇点,那么它们全不是汇点,反之也如此。这也就意味着一个SCC里面的点要么全是,要么全不是。在求出SCC并缩点后,任一个编号为A的SCC若存在指向编号为B的SCC的边,那么它里面所有点必不是汇点(因为编号为B的SCC不可能存在指向编号为A的SCC的边)。若编号为A的SCC没有到达其他SCC的路径,那么该SCC里面所有点必是汇点。因此判断的关键在于SCC的出度是否为0.
    思路:先用tarjan求出所有SCC,然后缩点后找出所有出度为0的SCC,并用数字存储点,升序排列后输出。

    #include<stdio.h>
    #include<string.h>
    #include<vector>
    #include<stack>
    #include<algorithm>
    #define MAX 21000
    #define INF 0x3f3f3f
    using namespace std;
    int cost[MAX];
    int low[MAX],dfn[MAX];
    int head[MAX],instack[MAX];
    int ans,n,m;
    int sccno[MAX],clock;//sccno用来记录当前点属于哪个scc,
    int scccnt;//记录总共有多少个scc
    stack<int>s;
    vector<int>newmap[MAX];//scc缩点之后储存新图
    vector<int>scc[MAX];//用来记录scc中的点
    int out[MAX];//记录scc的入度
    int ant[MAX];
    struct node
    {
        int beg,end,next;
    }edge[MAX];
    void init()
    {
        memset(head,-1,sizeof(head));
        ans=0;
    }
    void add(int u,int v)
    {
        edge[ans].beg=u;
        edge[ans].end=v;
        edge[ans].next=head[u];
        head[u]=ans++;
    }
    void getmap()
    {
        int i,j,a,b;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
        }
    }
    void tarjan(int u)
    {
        int v,i,j;
        low[u]=dfn[u]=++clock;
        s.push(u);
        instack[u]=1;
        for(i=head[u];i!=-1;i=edge[i].next)
        {
            v=edge[i].end;
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(instack[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u])
        {
            scccnt++;
            scc[scccnt].clear();//??
            while(1)
            {
                v=s.top();
                s.pop();
                instack[v]=0;
                sccno[v]=scccnt;
                scc[scccnt].push_back(v);
                if(v==u)
                break;
            }
        }
    }
    void find(int l,int r)
    {
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(sccno,0,sizeof(sccno));
        memset(instack,0,sizeof(instack));
        clock=scccnt=0;
        for(int i=l;i<=r;i++)
        {
            if(!dfn[i])
                tarjan(i);
        }
    }
    void suodian()
    {
        int i,j;
        for(i=1;i<=scccnt;i++)
        {
            newmap[i].clear();
           out[i]=0;
        }
        for(i=0;i<ans;i++)//遍历所有的边 
        {
            int u=sccno[edge[i].beg];//当前边的起点 
            int v=sccno[edge[i].end];//当前边的终点
            if(u!=v)//因为sccno中记录的是当前点属于哪个scc,所以u!=v证明不在同一个scc但是由一条边相连,
            {       //证明这两个scc联通
                newmap[u].push_back(v);//将scc中的点储存下来  ??
                out[u]++;//两个scc联通 则入度加一,
            }
        }
    } 
    void solve()
    {
    	int i,j,k=0;
    	for(i=1;i<=scccnt;i++)
    	{
    		if(out[i]==0)
    		{
    			for(j=0;j<scc[i].size();j++)
    			    ant[k++]=scc[i][j];
    		}
    	}
    	sort(ant,ant+k);
    	for(i=0;i<k;i++)
    	{
            if(i<k-1)
    		printf("%d ",ant[i]);
            else
            printf("%d",ant[i]);
    	}
    	printf("
    ");
    }
    int main()
    {
        int j,i;
        while(scanf("%d",&n),n)
        {
        	scanf("%d",&m);
        	
            init();
            getmap();
            find(1,n);
            suodian();
            solve();
        }
        return 0;
    }
    

      

  • 相关阅读:
    超酷幽默搞笑的摄影作品
    免费素材:超棒的PSD格式UI套件 Soft UI KIT
    【简报】使用3D过渡效果的图片幻灯:Adaptor
    一个超棒的CSS属性排序工具 CSScomb
    GBin1专题之Web热点#5
    一个超棒的消息提示jQuery插件 Noty
    分享12个超棒的免费PSD格式UI套件
    分享11个超棒的移动应用(mobile apps)开发解决方案
    超棒的仪表盘javascript类库 justGage
    为什么使用Dojo toolkit的十大原因
  • 原文地址:https://www.cnblogs.com/tonghao/p/4859563.html
Copyright © 2011-2022 走看看