poj 2762 Going from u to v or from v to u?(强连通分量+缩点重构图+拓扑排序)
http://poj.org/problem?id=2762
Going from u to v or from v to u?
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 12733 | Accepted: 3286 |
Description
In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the other. The son can either go from x to y, or from y to x. Wind promised that her tasks are all possible, but she actually doesn't know how to decide if a task is possible. To make her life easier, Jiajia decided to choose a cave in which every pair of rooms is a possible task. Given a cave, can you tell Jiajia whether Wind can randomly choose two rooms without worrying about anything?
Input
The first line contains a single integer T, the number of test cases. And followed T cases.
The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly.
The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly.
Output
The output should contain T lines. Write 'Yes' if the cave has the property stated above, or 'No' otherwise.
Sample Input
1 3 3 1 2 2 3 3 1
Sample Output
Yes
Source
POJ Monthly--2006.02.26,zgl & twb
【题解】:
这题题目大概意思就是说 判断两个点之间是不是能够走通,u到v 或者 v到u 都行,这题开始题目意思都理解错了,一开始以为就是一个判断强连通的题;
先求强连通分量;
然后将强连通分量缩点,重构图;
强连通分量内部的点肯定是可以两两到的,所以可以不用管了;
缩点后的图,然后进行拓扑排序,如果有两个及以上的点的入度为0,那么他们之间肯定是不可达的,所以不符合
如图:
【code】:
1 /** 2 Judge Status:Accepted Memory:4896K 3 Time:485MS Language:G++ 4 Code Lenght:2940B Author:cj 5 */ 6 7 #include<iostream> 8 #include<algorithm> 9 #include<stdio.h> 10 #include<string.h> 11 #include<vector> 12 #include<stack> 13 14 using namespace std; 15 #define N 1010 16 #define M 6060 17 18 struct Edge 19 { 20 int v; 21 int next; 22 int u; 23 }edge[M]; //M 开始传的N RE 24 25 int head[N],edge_cnt;//这一行变量是构造边的 26 27 int pre[N],lowlink[N],sccno[N],dfs_cnt,scc_cnt; //这两行是Tarjan算法求强连通分量用的 28 stack<int> stk; 29 30 vector<int> G[N]; //缩点重构图用的邻接链表 31 int in[N],mark[N][N]; //入度统计,以及重复边的标记 32 33 void init() 34 { 35 memset(head,-1,sizeof(head)); 36 edge_cnt = 0; 37 } 38 39 void addEdge(int a,int b) 40 { 41 edge[edge_cnt].v = b; 42 edge[edge_cnt].next = head[a]; 43 edge[edge_cnt].u = a; //这里记录a,重构图用,方便遍历所有边 44 head[a] = edge_cnt++; 45 } 46 47 void Tarjan(int u) //强连通分量 48 { 49 stk.push(u); 50 pre[u] = lowlink[u] = ++dfs_cnt; 51 int i; 52 for(i=head[u];i!=-1;i=edge[i].next) 53 { 54 int v = edge[i].v; 55 if(!pre[v]) 56 { 57 Tarjan(v); 58 lowlink[u] = min(lowlink[u],lowlink[v]); 59 } 60 else if(!sccno[v]) 61 { 62 lowlink[u] = min(lowlink[u],pre[v]); 63 } 64 } 65 if(pre[u]==lowlink[u]) 66 { 67 scc_cnt++; 68 int x; 69 do 70 { 71 x = stk.top(); 72 stk.pop(); 73 sccno[x] = scc_cnt; //sccno[x]表示下标为x的节点所在的第几个强连通分量 74 }while(x!=u); 75 } 76 } 77 78 void findscc(int n) 79 { 80 memset(pre,0,sizeof(pre)); 81 memset(lowlink,0,sizeof(lowlink)); 82 memset(sccno,0,sizeof(sccno)); 83 dfs_cnt = scc_cnt = 0; 84 while(!stk.empty()) //初始化 以防万一 85 { 86 stk.pop(); 87 } 88 int i; 89 for(i=1;i<=n;i++) if(!pre[i]) Tarjan(i); 90 } 91 92 void getNewMap() //重构缩点后的图,存入邻接链表G中 93 { 94 int i,j; 95 for(i=0;i<=scc_cnt;i++) 96 { 97 G[i].clear(); 98 in[i] = 0; 99 } 100 memset(mark,0,sizeof(mark)); 101 for(i=0;i<edge_cnt;i++) 102 { 103 int v = edge[i].v; 104 int u = edge[i].u; 105 if(sccno[u]!=sccno[v]) 106 { 107 if(!mark[u][v]) //重复边标记 108 { 109 G[sccno[u]].push_back(sccno[v]); 110 in[sccno[v]]++; //入度统计 111 } 112 mark[u][v] = 1; 113 } 114 } 115 } 116 117 int cntInid() //计算入度为0的位置,以及是不是一个 118 { 119 int i,cnt=0,id=0; 120 for(i=1;i<=scc_cnt;i++) 121 { 122 if(!in[i]) 123 { 124 cnt++; 125 id = i; 126 } 127 } 128 if(cnt==1) 129 return id; 130 return 0; 131 } 132 133 int isOK() //用拓扑排序判断是否可行 134 { 135 int id = cntInid(); 136 if(!id) return 0; 137 int i; 138 in[id] = -1; 139 for(i=0;i<G[id].size();i++) 140 { 141 in[G[id][i]]--; 142 } 143 if(G[id].size()>0) return isOK(); 144 return 1; 145 } 146 147 int main() 148 { 149 int t; 150 scanf("%d",&t); 151 while(t--) 152 { 153 int n,m; 154 scanf("%d%d",&n,&m); 155 int i; 156 init(); 157 for(i=0;i<m;i++) 158 { 159 int a,b; 160 scanf("%d%d",&a,&b); 161 addEdge(a,b); 162 } 163 findscc(n); //求强连通分量 164 if(scc_cnt==1) //如果只有一个那么肯定可行 165 { 166 puts("Yes"); 167 continue; 168 } 169 getNewMap(); //用强连通分量缩点,重构图 170 if(isOK()) puts("Yes"); //拓扑排序判断 171 else puts("No"); 172 } 173 return 0; 174 }