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. 

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

 
【题解】:
  
  这题题目大概意思就是说 判断两个点之间是不是能够走通,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 }

 

posted @ 2013-08-11 20:23  crazy_apple  阅读(239)  评论(0编辑  收藏  举报