poj2762&&poj2186 强联通分量

Going from u to v or from v to u?
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 11440 Accepted: 2929

Description

In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the other. The son can either go from x to y, or from y to x. Wind promised that her tasks are all possible, but she actually doesn't know how to decide if a task is possible. To make her life easier, Jiajia decided to choose a cave in which every pair of rooms is a possible task. Given a cave, can you tell Jiajia whether Wind can randomly choose two rooms without worrying about anything?

Input

The first line contains a single integer T, the number of test cases. And followed T cases.
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

The output should contain T lines. Write 'Yes' if the cave has the property stated above, or 'No' otherwise.

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搜索,删除能够遍历到的顶点,这些顶点构成一个强连通分量

④如果还有顶点没有删除,继续执行③,直到算法结束。

View Code
  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算法:

View Code
 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算法:

View Code
 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 }

 

转载于:https://www.cnblogs.com/-sunshine/archive/2012/10/04/2711185.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值