Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 11440 | Accepted: 2929 |
Description
Input
The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly.
Output
Sample Input
1 3 3 1 2 2 3 3 1
Sample Output
Yes
题意:为给出任意两个城市x,y,看是否存在一条从x到y的路径或者是从y到x的路径,存在这输出yes,给的边是有向边。
思路:求有向图的强连通分量+拓扑排序。一般有两种方法,一种是Tarjan算法,一种是Kosaraju算法,一种是gabow算法。
百度百科:http://baike.baidu.com/view/1976645.htm
Tarjan算法是基于DFS算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中的未处理的结点加入一个栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。当dfn[u]==low[u]时,以u为根的搜索子树上所有结点是一个强连通分量。
其中dfn和low分别表示什么还是提前了解一下吧:dfn数组表示深度优先数(访问次序),low[u]表示从u或者u的子孙出发通过回边可以到达的最低深度优先数。
low[u]=min{ dfn[u],
min{ low[w] | w是u的一个子女},
min{ dfn[v] | v与u邻接,且(u,v)是一条回边 }
感觉网上大都是Tarjan算法,这里不想介绍了。学一下Kosaraju算法:
Kosaraju算法是基于对有向图G及其逆图G' (各边反向得到的有向图)进行两次DFS的方法,时间复杂度是O(N+M),与Tarjan算法比较,Kosaraju算法更直观。
Kosaraju算法原理:
①对原图G进行深度优先搜索,并记录每个顶点的dfn的值
②将图G的各边进行反向,得到其逆图G'
③选择从当前dfn最大的顶点出发,对逆图G' 进行DFS搜索,删除能够遍历到的顶点,这些顶点构成一个强连通分量
④如果还有顶点没有删除,继续执行③,直到算法结束。
1 #include <queue> 2 #include <iostream> 3 #include <cstring> 4 #include <stdio.h> 5 using namespace std; 6 #define M 1010 7 int min(int a,int b){return a<b?a:b;} 8 struct Edge{ 9 int v,to; 10 }edge[6*M],edge1[6*M]; 11 int head[M],head1[M]; 12 int edgeNum,edgeNum1; 13 int cnt,scnt,begin,n,m; 14 int low[M],dfn[M],stack[M],id[M],in[M]; 15 //bool inStack[M]; 16 void add(int a,int b){ 17 edge[edgeNum].v=b; 18 edge[edgeNum].to=head[a]; 19 head[a]=edgeNum++; 20 } 21 void add1(int a,int b){ 22 edge1[edgeNum1].v=b; 23 edge1[edgeNum1].to=head1[a]; 24 head1[a]=edgeNum1++; 25 } 26 void dfs(int x){ 27 low[x]=dfn[x]=++cnt; 28 stack[++begin]=x; 29 //inStack[x]=1; 30 int v; 31 for(int i=head[x];i!=-1;i=edge[i].to){ 32 v=edge[i].v; 33 if(!dfn[v]){ 34 dfs(v); 35 low[x]=min(low[v],low[x]); 36 } 37 else if(/*inStack[v]*/!id[v]){ 38 low[x]=min(dfn[v],low[x]); 39 } 40 } 41 if(low[x]==dfn[x]){ 42 scnt++; 43 do{ 44 v=stack[begin--]; 45 //inStack[v]=0; 46 id[v]=scnt; 47 }while(v!=x); 48 } 49 return ; 50 } 51 void Tarjan(){ 52 cnt=scnt=begin=0; 53 memset(dfn,0,sizeof(dfn)); 54 //memset(inStack,0,sizeof(inStack)); 55 for(int i=1;i<=n;i++){ 56 if(!dfn[i]) dfs(i); 57 } 58 return ; 59 } 60 bool toposort(){ 61 queue<int> que; 62 for(int i=1;i<=scnt;i++){ 63 if(!in[i]) que.push(i); 64 } 65 if(que.size()>1) return 0; 66 while(!que.empty()){ 67 int now=que.front(); 68 que.pop(); 69 for(int i=head1[now];i!=-1;i=edge1[i].to){ 70 int v=edge1[i].v; 71 in[v]--; 72 if(in[v]==0) que.push(v); 73 } 74 if(que.size()>1) return 0; 75 } 76 return 1; 77 } 78 int main(){ 79 int t,a,b; 80 scanf("%d",&t); 81 while(t--){ 82 scanf("%d%d",&n,&m); 83 edgeNum=edgeNum1=0; 84 memset(id,0,sizeof(id)); 85 memset(head,-1,sizeof(head)); 86 for(int i=0;i<m;i++){ 87 scanf("%d%d",&a,&b); 88 add(a,b); 89 } 90 Tarjan(); 91 if(scnt==1) {printf("Yes\n");continue;} 92 memset(in,0,sizeof(in)); 93 memset(head1,-1,sizeof(head1)); 94 for(int i=1;i<=n;i++) 95 for(int j=head[i];j!=-1;j=edge[j].to){ 96 int v=edge[j].v; 97 if(id[i]!=id[v]){ 98 in[id[v]]++; 99 add1(id[i],id[v]); 100 } 101 } 102 if(toposort()) printf("Yes\n"); 103 else printf("No\n"); 104 } 105 }
POJ2186更简单,直接只求强连通分量,看缩点后的图有几个出度为0的点,如果大于1个,则结果就是0,否则,看出度为0的那个点是由原来多少个点缩点后得到的,输出即可
Tarjan算法:
1 #include <queue> 2 #include <iostream> 3 #include <cstring> 4 #include <stdio.h> 5 using namespace std; 6 #define M 10010 7 int min(int a,int b){return a<b?a:b;} 8 struct Edge{ 9 int v,to; 10 }edge[5*M]; 11 int head[M]; 12 int edgeNum; 13 int cnt,scnt,begin,n,m; 14 int low[M],dfn[M],stack[M],id[M],out[M]; 15 int ans[M]; 16 void add(int a,int b){ 17 edge[edgeNum].v=b; 18 edge[edgeNum].to=head[a]; 19 head[a]=edgeNum++; 20 } 21 void dfs(int x){ 22 low[x]=dfn[x]=++cnt; 23 stack[++begin]=x; 24 int v; 25 for(int i=head[x];i!=-1;i=edge[i].to){ 26 v=edge[i].v; 27 if(!dfn[v]){ 28 dfs(v); 29 low[x]=min(low[v],low[x]); 30 } 31 else if(!id[v]){ 32 low[x]=min(dfn[v],low[x]); 33 } 34 } 35 if(low[x]==dfn[x]){ 36 scnt++; 37 int tmp=0; 38 do{ 39 tmp++; 40 v=stack[begin--]; 41 id[v]=scnt; 42 }while(v!=x); 43 ans[scnt]=tmp; 44 } 45 return ; 46 } 47 void Tarjan(){ 48 cnt=scnt=begin=0; 49 memset(dfn,0,sizeof(dfn)); 50 for(int i=1;i<=n;i++){ 51 if(!dfn[i]) dfs(i); 52 } 53 return ; 54 } 55 int main(){ 56 int t,a,b; 57 while(scanf("%d%d",&n,&m)!=EOF){ 58 59 edgeNum=0; 60 memset(id,0,sizeof(id)); 61 memset(head,-1,sizeof(head)); 62 for(int i=0;i<m;i++){ 63 scanf("%d%d",&a,&b); 64 add(a,b); 65 } 66 Tarjan(); 67 if(scnt==1) {printf("%d\n",n);continue;} 68 memset(out,0,sizeof(out)); 69 for(int i=1;i<=n;i++) 70 for(int j=head[i];j!=-1;j=edge[j].to){ 71 int v=edge[j].v; 72 if(id[i]!=id[v]){ 73 out[id[i]]++; 74 } 75 } 76 int res=0; 77 for(int i=1;i<=scnt;i++){ 78 if(!out[i]){ 79 if(!res) res=ans[i]; 80 else {res=0;break;} 81 } 82 } 83 printf("%d\n",res); 84 } 85 }
Kosaraju算法:
1 #include <cstring> 2 #include <stdio.h> 3 #define N 10005 4 #define maxm 50005 5 struct Edge{ 6 int v,next; 7 }edge[maxm],opedge[maxm]; 8 int signnum,n,m,edgeNum,cnt; 9 int out[N],ans[N],head[N],ophead[N],pos[N*2],sig[N]; 10 bool flag[N]; 11 void addedge(int a, int b){ 12 edge[edgeNum].v=b; 13 edge[edgeNum].next=head[a]; 14 head[a]=edgeNum; 15 opedge[edgeNum].v=a; 16 opedge[edgeNum].next=ophead[b]; 17 ophead[b]=edgeNum++; 18 } 19 void input(){ 20 edgeNum=0; 21 memset(head,-1,sizeof(head)); 22 memset(ophead,-1,sizeof(ophead)); 23 memset(flag,0,sizeof(flag)); 24 for(int i=0;i<m;i++){ 25 int a,b; 26 scanf("%d%d",&a,&b); 27 a--; 28 b--; 29 addedge(a,b); 30 } 31 } 32 void dfs(int a){ 33 flag[a]=true; 34 pos[cnt]=a; 35 cnt++; 36 for (int i=head[a];i!=-1;i=edge[i].next) 37 if (!flag[edge[i].v]) 38 dfs(edge[i].v); 39 pos[cnt]=a; 40 cnt++; 41 } 42 void rdfs(int a){ 43 flag[a]=true; 44 sig[a]=signnum; 45 for(int i=ophead[a];i!=-1;i=opedge[i].next) 46 if(!flag[opedge[i].v]) 47 rdfs(opedge[i].v); 48 } 49 int main(){ 50 while(scanf("%d%d",&n,&m)!=EOF){ 51 input(); 52 cnt=1; 53 for(int i=0;i<n;i++) 54 if(!flag[i]) 55 dfs(i); 56 memset(flag,0,sizeof(flag)); 57 signnum=0; 58 for(int i=2*n;i>0;i--) 59 if(!flag[pos[i]]){ 60 rdfs(pos[i]); 61 signnum++; 62 } 63 memset(out,0,sizeof(out)); 64 memset(ans,0,sizeof(ans)); 65 for(int i=0;i<n;i++){ 66 ans[sig[i]]++; 67 for(int j=head[i];j!=-1;j=edge[j].next) 68 if(sig[i]!=sig[edge[j].v]){ 69 out[sig[i]]++; 70 } 71 } 72 int res=0; 73 for(int i=0;i<signnum;i++) 74 { 75 if(!out[i]){ 76 if(!res) res=ans[i]; 77 else {res=0;break;} 78 } 79 } 80 printf("%d\n",res); 81 } 82 return 0; 83 }