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 v, v 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.
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 v, v 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,并用数字存储点,升序排列后输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #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( "\n" ); } int main() { int j,i; while (scanf( "%d" ,&n),n) { scanf( "%d" ,&m); init(); getmap(); find(1,n); suodian(); solve(); } return 0; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步