HDU1269(有向图缩点模板题)
迷宫城堡
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11070 Accepted Submission(s): 4948
Problem Description
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
Input
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
Output
对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。
Sample Input
3 3
1 2
2 3
3 1
3 3
1 2
2 3
3 2
0 0
Sample Output
Yes
No
有向图缩点模板题
模板1:kosaraju
#include"cstdio" #include"cstring" #include"vector" using namespace std; const int MAXN=10005; vector<int> G[MAXN]; vector<int> rG[MAXN]; vector<int> vs; int V,E; int vis[MAXN]; inline int max(int a,int b) { return a > b? a: b; } void dfs(int u) { vis[u]=1; for(int i=0;i<G[u].size();i++) if(!vis[G[u][i]]) dfs(G[u][i]); vs.push_back(u); } void rdfs(int u) { vis[u]=1; for(int i=0;i<rG[u].size();i++) if(!vis[rG[u][i]]) rdfs(rG[u][i]); } bool scc() { memset(vis,0,sizeof(vis)); for(int i=1;i<=V;i++) if(!vis[i]) dfs(i); memset(vis,0,sizeof(vis)); int k=0; for(int i=vs.size()-1;i>=0;i--) if(!vis[vs[i]]) { rdfs(vs[i]); k++; if(k>1) return false; } return true; } int main() { while(scanf("%d%d",&V,&E)!=EOF&&V) { vs.clear(); for(int i=1;i<=V;i++) { G[i].clear(); rG[i].clear(); } for(int i=0;i<E;i++) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); rG[v].push_back(u); } if(scc()) { printf("Yes\n"); } else { printf("No\n"); } } return 0; }
模板2:tarjan
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int MAXN=10005; vector<int> mp[MAXN]; int n,m; int dfn[MAXN],low[MAXN],time; int stack[MAXN],top; bool ins[MAXN]; int cnt=0; void tarjan(int u) { dfn[u]=low[u]=++time; stack[top++]=u; ins[u]=true; for(int i=0;i<mp[u].size();i++) { int v=mp[u][i]; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(ins[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { int v; cnt++; do{ v=stack[--top]; ins[v]=false; }while(u!=v); } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0) break; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ins,false,sizeof(ins)); time=0; top=0; cnt=0; for(int i=1;i<=n;i++) mp[i].clear(); for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); mp[u].push_back(v); } for(int i=1;i<=n;i++) { if(!dfn[i]) { tarjan(i); } } if(cnt==1) printf("Yes\n"); else printf("No\n"); } }
该题目也可用并查集做。
#include"cstdio" #include"cstring" using namespace std; const int MAXN=100005; int V,E; int par[2][MAXN];//par[0][MAXN]记录正向边,par[1][MAXN]记录反向边 int fnd(int x,int type) { if(par[type][x]==x) { return x; } return par[type][x]=fnd(par[type][x],type); } void unite(int x,int y) { if(x>1) par[0][x]=fnd(y,0);//将结点1作为根结点 if(y>1) par[1][y]=fnd(x,1); } bool judge() { for(int i=2;i<=V;i++) if(fnd(i,0)!=1||fnd(i,1)!=1) return false;//若所有结点(除1)沿正向边均能到达1结点且将边反向后也均能到达1结点,那么改图为强连通图 return true; } int main() { while(scanf("%d%d",&V,&E)!=EOF&&V) { for(int i=1;i<=V;i++) par[0][i]=par[1][i]=i; for(int i=0;i<E;i++) { int u,v; scanf("%d%d",&u,&v); unite(u,v); } if(judge()) printf("Yes\n"); else printf("No\n"); } return 0; }