POJ 2762 Going from u to v …(强连通分量+拓扑排序)
http://poj.org/problem?id=2762
题意:
给你一个有向图,如果对于图中的任意一对点u和v(即所有两点都需要满足)都有一条从u到v的路或从v到u的路,那么就输出’Yes’,否则输出’No’.
分析:
首先求出该图的所有强连通分量,对于分量中的任意两点肯定都是有路的。我们只需要判断不同分量内的点是否有路即可。
把所有分量缩点,构成一个新的DAG图。现在的问题变成了:该DAG图是否对于任意两点都存在一条路。(想想为什么这个问题与原问题等价)
什么时候DAG图满足上述条件呢?多画几个图可以知道该DAG图只能是一条链的时候才行(自己画图验证一下,注意DAG图无环),即该DAG图拓扑排序时产生的是全序排列。(队列中始终只有一个元素)
AC代码:
#include<cstdio> #include<cstring> #include<stack> #include<algorithm> #include<vector> using namespace std; const int maxn=1000+10; int n,m; vector<int> G[maxn],G2[maxn]; stack<int> S; int dfs_clock, scc_cnt; int pre[maxn],low[maxn],sccno[maxn]; int in[maxn];//新图的入度 void dfs(int u) { pre[u]=low[u]=++dfs_clock; S.push(u); for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(!pre[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(!sccno[v]) low[u]=min(low[u],pre[v]); } if(low[u]==pre[u]) { scc_cnt++; while(true) { int x=S.top(); S.pop(); sccno[x]=scc_cnt; if(x==u) break; } } } void find_scc(int n) { scc_cnt=dfs_clock=0; memset(pre,0,sizeof(pre)); memset(sccno,0,sizeof(sccno)); for(int i=1;i<=n;i++) if(!pre[i]) dfs(i); } bool topo() { stack<int> Q; int sum=0;//记录出Q的节点总数 for(int i=1;i<=scc_cnt;i++) if(in[i]==0) Q.push(i); while(!Q.empty()) { if(Q.size()>1) return false; int u=Q.top(); Q.pop(); sum++; for(int i=0;i<G2[u].size();i++) { int v=G2[u][i]; if(--in[v]==0) Q.push(v); } } return sum==scc_cnt; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) G[i].clear(); while(m--) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); } find_scc(n); //下面是对新图拓扑排序 bool map[maxn][maxn]; memset(map,0,sizeof(map)); for(int i=1;i<=scc_cnt;i++) G2[i].clear(); memset(in,0,sizeof(in)); for(int u=1;u<=n;u++) for(int i=0;i<G[u].size();i++) { int v=G[u][i]; int x=sccno[u],y=sccno[v]; if(!map[x][y] && x!=y) { G2[x].push_back(y); map[x][y]=true; in[y]++; } } printf("%s\n",topo()?"Yes":"No"); } return 0; }