POJ 2762 Going from u to v or from v to u?

来源:互联网 发布:淘宝网哪家蜜蜡保真 编辑:程序博客网 时间:2024/05/29 16:41

题目链接:http://poj.org/problem?id=2762

题意:给出一个图及其相连的边,判断是否任意两点之间都是相通的(能从x到y或者能从y到x)。

题解:可以知道每个强连通分中的点必然都能够相互到达,所以先对图进行缩点,缩点之后是一个有向无环图DAG,构造新图,任意两个点相互可达转化为任意两个连通分量相互可达,即新图中任意两个点可达。

对新图进行拓扑排序,排序之后如果任意相邻两点之间存在边,相当于存在一条从一个点途径其余所有点到达另一个点的一条“”,则这个新图任意两点都是是相互可达的。

想象一下拓扑排序后相邻两点不存在边的情况,其实就是当寻找当前入度为0的点的时候找到的点不止一个,则这两个点必定不连通。如下图,当1,2排序之后,发现有两个入度为0的点3和4,则这两个点排完序之后虽然相邻,但是并不相通。


但是其实这道题不用实际的拓扑排序,因为我们知道其实Tarjan算法求出的连通分量的顺序就是这个新图的拓扑排序的逆。(可以看这篇博客)

代码:

用拓扑排序。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<stack>using namespace std;const int MAX=1000+10;vector<int> g[MAX];stack<int> st;int dfn[MAX],low[MAX],instack[MAX];int sccno[MAX];int dfs_cnt,scc_cnt;int G[MAX][MAX];int in[MAX];int topo[MAX];int n,m;void dfs(int u){dfn[u]=low[u]=++dfs_cnt;st.push(u);instack[u]=1;for(int i=0;i<g[u].size();i++){int v=g[u][i];if(!dfn[v]){dfs(v);low[u]=min(low[u],low[v]);}else if(instack[v]){low[u]=min(low[u],dfn[v]);}}if(dfn[u]==low[u]){int v;scc_cnt++;do{v=st.top();st.pop();instack[v]=0;sccno[v]=scc_cnt;}while(u!=v);}}void tarjan(){dfs_cnt=scc_cnt=0;memset(dfn,0,sizeof(dfn));memset(instack,0,sizeof(instack));memset(sccno,0,sizeof(sccno));while(!st.empty()) st.pop();for(int i=1;i<=n;i++){if(!dfn[i]) dfs(i); }}void toposort(){for(int i=1;i<=scc_cnt;i++){for(int j=1;j<=scc_cnt;j++){in[j]+=G[i][j];}}for(int k=0;k<scc_cnt;k++){int i,j;for(i=1;in[i]&&i<=scc_cnt;i++);if(i>scc_cnt) return ;topo[k]=i;in[i]=-1;for(j=1;j<=scc_cnt;j++){if(G[i][j]){in[j]--;}}}}int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);int T;scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);memset(G,0,sizeof(G));memset(in,0,sizeof(in));memset(topo,0,sizeof(topo));for(int i=1;i<=n;i++)g[i].clear();for(int i=0;i<m;i++){int a,b;scanf("%d%d",&a,&b);g[a].push_back(b);}tarjan();for(int i=1;i<=n;i++)for(int k=0;k<g[i].size();k++){int j=g[i][k];if(sccno[i]!=sccno[j])G[sccno[i]][sccno[j]]=1;}toposort();int flag=1;for(int i=1;i<scc_cnt;i++){if(!G[topo[i-1]][topo[i]]){flag=0;break;}}printf("%s\n",flag?"Yes":"No");}return 0;}
不用拓扑排序,直接判断:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#include <stack>using namespace std;const int MAX=1000+10;vector<int> g[MAX];stack<int> st;int dfn[MAX],low[MAX],instack[MAX];int sccno[MAX];int dfs_cnt,scc_cnt;int G[MAX][MAX];int n,m;void dfs(int u){dfn[u]=low[u]=++dfs_cnt;st.push(u);instack[u]=1;for(int i=0;i<g[u].size();i++){int v=g[u][i];if(!dfn[v]){dfs(v);low[u]=min(low[u],low[v]);}else if(instack[v]){low[u]=min(low[u],dfn[v]);}}if(dfn[u]==low[u]){int v;scc_cnt++;do{v=st.top();st.pop();instack[v]=0;sccno[v]=scc_cnt;}while(u!=v);}}void tarjan(){dfs_cnt=scc_cnt=0;memset(dfn,0,sizeof(dfn));memset(instack,0,sizeof(instack));memset(sccno,0,sizeof(sccno));while(!st.empty()) st.pop();for(int i=1;i<=n;i++){if(!dfn[i]) dfs(i); }}int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);int T;scanf("%d",&T);while(T--){scanf("%d%d",&n,&m);memset(G,0,sizeof(G));for(int i=1;i<=n;i++)g[i].clear();for(int i=0;i<m;i++){int a,b;scanf("%d%d",&a,&b);g[a].push_back(b);}tarjan();for(int i=1;i<=n;i++)for(int k=0;k<g[i].size();k++){int j=g[i][k];if(sccno[i]!=sccno[j])G[sccno[i]][sccno[j]]=1;}int flag=1;for(int i=2;i<=scc_cnt;i++){if(!G[i][i-1]){flag=0;break;}}printf("%s\n",flag?"Yes":"No");}return 0;}



0 0
原创粉丝点击