POJ 2762 Going from u to v or from v to u?(半连通图)
题目大意
给你一个 n(0<n<1001) 个节点 m(m<6000) 条边的有向图,对于图中的任意两个节点 u 和 v,问能否从 u 走到 v 或者从 v 走到 u
PS:这其实是《算法导论》中讲“强连通分量”的最后一道习题
做法分析
由于强连通分量重的点肯定能够满足题目的要求,所以可以先将图“化简”,变成 DAG(Directed Acyclic Graph)
首先:如果当前的 DAG 有不止一个入读为 0 的点,那么这些点之间肯定不能相互到达,还要明确的是,当前的 DAG 中至少有一个节点的入读为 0(如果没有节点的入读为 0,这些节点肯定能够形成环,和 DAG 中的 Acyclic 矛盾,前面的缩点工作做的就不彻底)
也就是说,现在我们要考虑的就是只有一个点的入读为 0 的情况,这个入读为 0 的点肯定能够到达图中所有的点!
然后我们将这个入读为 0 的点删掉,考虑剩下的 DAG,由于我们只需要考虑两个点之间是否可以从一个点到达另一个点,那么,当前我们要考虑的就是当前的 DAG 中,入读为 0 的点是否只有一个就行了!(还是一样的道理,如果有两个以上的点,这些点之间是不可到达的)
于是思路清晰了:
向将原图缩点,形成 DAG(重新建图是必须的啦)
对新建好的图拓扑排序:
每次判断当前是否只有一个点的入读为 0,如果不止一个,跳出拓扑排序过程,输出 No
将那个入读为 0 的点从新图中删掉,再进行下一轮判断
如果当前的 DAG 中只剩一个节点,跳出拓扑排序过程,输出 Yes
缩点当然用 Tarjan 了
参考代码
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <vector> 5 #include <stack> 6 7 using namespace std; 8 9 const int N=1006; 10 11 vector <int> arc[N], adj[N]; 12 int n, m, t, T, ind; 13 int dfn[N], low[N], id[N], in[N]; 14 bool vs[N]; 15 stack <int> s; 16 17 void tarjan(int u) 18 { 19 vs[u]=1, s.push(u); 20 dfn[u]=low[u]=T++; 21 int len=(int)arc[u].size(); 22 for(int i=0; i<len; i++) 23 { 24 int v=arc[u][i]; 25 if(dfn[v]==-1) 26 { 27 tarjan(v); 28 if(low[u]>low[v]) low[u]=low[v]; 29 } 30 else if(vs[v] && low[u]>dfn[v]) low[u]=dfn[v]; 31 } 32 if(low[u]==dfn[u]) 33 { 34 for(int v; 1; ) 35 { 36 v=s.top(); 37 s.pop(), vs[v]=0; 38 id[v]=ind; 39 if(v==u) break; 40 } 41 ind++; 42 } 43 } 44 45 int main() 46 { 47 scanf("%d", &t); 48 for(int ca=1; ca<=t; ca++) 49 { 50 scanf("%d%d", &n, &m); 51 for(int i=0; i<=n; i++) dfn[i]=-1, vs[i]=0, arc[i].clear(); 52 while(!s.empty()) s.pop(); 53 for(int i=0, a, b; i<m; i++) 54 { 55 scanf("%d%d", &a, &b); 56 arc[a].push_back(b); 57 } 58 ind=T=0; 59 for(int i=1; i<=n; i++) if(dfn[i]==-1) tarjan(i); 60 for(int i=0; i<ind; i++) adj[i].clear(), in[i]=0, vs[i]=0; 61 for(int i=1; i<=n; i++) 62 { 63 int len=(int)arc[i].size(), u=id[i]; 64 for(int j=0; j<len; j++) 65 { 66 int v=id[arc[i][j]]; 67 if(u!=v) adj[u].push_back(v), in[v]++; 68 } 69 } 70 int tot=ind, flag=1; 71 while(flag && tot>1) 72 { 73 int p=-1, cnt=0; 74 for(int i=0; i<ind; i++) 75 if(!vs[i] && in[i]==0) p=i, cnt++; 76 if(cnt!=1) 77 { 78 flag=0; 79 break; 80 } 81 int len=(int)adj[p].size(); 82 for(int i=0; i<len; i++) in[adj[p][i]]--; 83 tot--, vs[p]=1; 84 } 85 if(flag) printf("Yes\n"); else printf("No\n"); 86 } 87 return 0; 88 }
AC通道
POJ 2762 Going from u to v or from v to u?