HDU 1269 迷宫城堡(向量)(Tarjan模版题)
迷宫城堡
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 19017 Accepted Submission(s):
8328
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结束。
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include<stack> using namespace std; #define MAXN 10010 #define MAXM 100010 stack<int>s; int head[MAXN],dfn[MAXN], low[MAXN], belong[MAXM]; int instack[10010]; // instack[]为是否在栈中的标记数组 int n, m, cnt, scnt, top, tot; struct Edge { int v, next; }e[MAXM]; //边结点数组 void add(int u,int v) { e[++cnt].v=v; e[cnt].next=head[u]; head[u]=cnt; } void init() { cnt = 0; scnt=0; //初始化连通分量标号 memset(head, -1, sizeof(head)); memset(dfn, 0, sizeof(dfn)); //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用 while(!s.empty()) s.pop(); } void Tarjan(int v) { int min,t; dfn[v]=low[v]=cnt++; instack[v]=1; s.push(v); for(int i=head[v];i!=-1;i=e[i].next) { int j=e[i].v; if(!dfn[j])//未被访问 { Tarjan(j); // 更新结点v所能到达的最小次数层 if(low[v]>low[j]) low[v]=low[j]; } else if(instack[j]&&low[v]>dfn[j]) {//如果j结点在栈内, low[v]=dfn[j]; } } if(dfn[v]==low[v]) {//如果节点v是强连通分量的根 scnt++; do { t=s.top(); s.pop(); instack[t]=0; belong[t]=scnt; } while(t!=v); } } int main() { while(scanf("%d%d",&n,&m) && (n || m)) { init(); while(m--) { int u,v; scanf("%d%d", &u, &v); add(u,v); } cnt=1; for(int i = 1; i <= n; i++) //枚举每个结点,搜索连通分量 { if(!dfn[i]) //未被访问 { Tarjan(i); //则找i结点的连通分量 } } if(scnt == 1) printf("Yes\n"); //只有一个强连通分量,说明此图各个结点都可达 else printf("No\n"); } return 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
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include<stack> using namespace std; #define MAXN 10010 #define MAXM 100010 stack<int>s; int head[MAXN],dfn[MAXN], low[MAXN], belong[MAXM]; int instack[10010]; // instack[]为是否在栈中的标记数组 int n, m, cnt, scnt, top, tot; struct Edge { int v, next; }e[MAXM]; //边结点数组 void add(int u,int v) { e[++cnt].v=v; e[cnt].next=head[u]; head[u]=cnt; } void init() { cnt = 0; scnt=0; //初始化连通分量标号,次序计数器,栈顶指针为0 memset(head, -1, sizeof(head)); memset(dfn, 0, sizeof(dfn)); //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用 while(!s.empty()) s.pop(); } void Tarjan(int v) { int min,t; dfn[v]=low[v]=cnt++; instack[v]=1; s.push(v); for(int i=head[v];i!=-1;i=e[i].next) { int j=e[i].v; if(!dfn[j]) { Tarjan(j); if(low[v]>low[j]) low[v]=low[j]; } else if(instack[j]&&low[v]>dfn[j]) { low[v]=dfn[j]; } } if(dfn[v]==low[v]) { scnt++; do { t=s.top(); s.pop(); instack[t]=0; belong[t]=scnt; } while(t!=v); } } int main() { while(scanf("%d%d",&n,&m) && (n || m)) { init(); while(m--) { int u,v; scanf("%d%d", &u, &v); add(u,v); } cnt=1; for(int i = 1; i <= n; i++) //枚举每个结点,搜索连通分量 { if(!dfn[i]) //未被访问 { Tarjan(i); //则找i结点的连通分量 } } if(scnt == 1) printf("Yes\n"); //只有一个强连通分量,说明此图各个结点都可达 else printf("No\n"); } return 0; }
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cstdlib> 5 using namespace std; 6 7 #define MAXN 10010 8 #define MAXM 100010 9 10 struct Edge 11 { 12 int v, next; 13 }edge[MAXM]; //边结点数组 14 15 int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM]; 16 // first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组,Belong[]为每个结点所对应的强连通分量标号数组 17 // Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号 18 int instack[10010]; // instack[]为是否在栈中的标记数组 19 int n, m, cnt, scnt, top, tot; 20 21 void init() 22 { 23 cnt = 0; 24 scnt = top = tot = 0; //初始化连通分量标号,次序计数器,栈顶指针为0 25 memset(first, -1, sizeof(first)); 26 memset(DFN, 0, sizeof(DFN)); //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用 27 } 28 29 void read_graph(int u, int v) //构建邻接表 30 { 31 edge[tot].v = v; 32 edge[tot].next = first[u]; 33 first[u] = tot++; 34 } 35 36 void Tarjan(int v) //Tarjan算法求有向图的强连通分量 37 { 38 int min, t; 39 DFN[v] = Low[v] = ++tot; //cnt为时间戳 40 instack[v] = 1; //标记在栈中 41 stack[top++] = v; //入栈 42 for(int e = first[v]; e != -1; e = edge[e].next) 43 { //枚举v的每一条边 44 int j = edge[e].v; //v所邻接的边 45 if(!DFN[j]) 46 { //未被访问 47 Tarjan(j); //继续向下找 48 if(Low[v] > Low[j]) Low[v] = Low[j]; // 更新结点v所能到达的最小次数层 49 } 50 else if(instack[j] && DFN[j] < Low[v]) 51 { //如果j结点在栈内, 52 Low[v] = DFN[j]; 53 } 54 } 55 if(DFN[v] == Low[v]) 56 { //如果节点v是强连通分量的根 57 scnt++; //连通分量标号加1 58 do 59 { 60 t = stack[--top]; //退栈 61 instack[t] = 0; //标记不在栈中 62 Belong[t] = scnt; //出栈结点t属于cnt标号的强连通分量 63 }while(t != v); //直到将v从栈中退出 64 } 65 } 66 67 void solve() 68 { 69 for(int i = 1; i <= n; i++) //枚举每个结点,搜索连通分量 70 if(!DFN[i]) //未被访问 71 Tarjan(i); //则找i结点的连通分量 72 } 73 74 int main() 75 { 76 while(scanf("%d%d",&n,&m) && (n || m)) 77 { 78 init(); 79 while(m--) 80 { 81 int u, v; 82 scanf("%d%d", &u, &v); 83 read_graph(u, v); 84 } 85 solve(); //求强连通分量 86 if(scnt == 1) printf("Yes\n"); //只有一个强连通分量,说明此图各个结点都可达 87 else printf("No\n"); 88 } 89 return 0; 90 }
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<vector> using namespace std; vector<int>gh1[100005];//向量 vector<int>gh2[100005]; int cnt; int vir[100005]; void dfs1(int n) { int x; vir[n] = 1; cnt++; for (int i = 0; i<gh1[n].size(); i++) { x = gh1[n].at(i); if (vir[x] == 0) dfs1(x); } } void dfs2(int n) { int x; vir[n] = 1; cnt++; for (int i = 0; i<gh2[n].size(); i++) { x = gh2[n].at(i); if (vir[x] == 0) dfs2(x); } } int main() { int m, n, a, b; while (~scanf("%d%d", &n, &m) && (m || n)) { for (int i = 0; i <= n; i++) { gh1[i].clear(); gh2[i].clear(); } for (int i = 1; i <= m; i++) { scanf("%d%d", &a, &b); gh1[a].push_back(b); gh2[b].push_back(a); } memset(vir, 0, sizeof(vir)); cnt = 0; dfs1(1);//正的走一次 if (cnt != n) { printf("No\n"); continue; } memset(vir, 0, sizeof(vir)); cnt = 0; dfs2(1);//反的走一次 if (cnt != n) { printf("No\n"); continue; } printf("Yes\n"); } return 0; }