tarjan算法(求强连通子块,缩点)

tarjan算法求图中的强连通子图的个数。

 1 #include<iostream>
 2 
 3 #include<stack>
 4 #include<queue>
 5 #include<string>
 6 #include<cstring>
 7 #include<algorithm>
 8 #include<cmath>
 9 # define maxn 100005
10 using namespace std;
11 vector<int>wakaka[maxn];
12 stack<int>q;
13 int  low[maxn];
14 int dfn[maxn];
15 int vis[maxn];
16 int num,ans;
17 void tarjan(int u)//u始终代表父亲节点
18 {
19     low[u]=dfn[u]=++num;
20     q.push(u);
21     vis[u]=1;
22     int len=wakaka[u].size();
23     for(int i=0; i<len; i++)
24     {
25         int v=wakaka[u][i];
26         if(vis[v]==0)
27         {
28             tarjan(v);
29             low[u]=min(low[u],low[v]);
30         }
31         if(vis[v])
32         {
33             low[u]=min(low[u],dfn[v]);
34         }
35     }
36     if(low[u]==dfn[u])
37     {
38         ans++;
39         int top;
40         do
41         {
42             top=q.top();
43             q.pop();
44             vis[top]=-1;
45         }
46         while(u!=top);
47     }
48 }
49 int main()
50 {
51     int n,m;
52     while(cin>>n>>m&&(n+m))
53     {
54         for(int i=1; i<=n; i++)
55         {
56             wakaka[i].clear();
57         }
58         ans=num=0;
59         while(!q.empty())q.pop();
60         memset(vis,0,sizeof(vis));
61         for(int i=1; i<=m; i++)
62         {
63             int u,v;
64             cin>>u>>v;
65             wakaka[u].push_back(v);
66         }
67         for(int i=1; i<=n; i++)
68         {
69             if(vis[i]==0)
70             {
71                 if(ans>=2){
72                   break;
73                   }
74                       tarjan(i);
75             }
76         }
77         if(ans==1)
78         cout<<"Yes"<<endl;
79         else {
80         cout<<"No"<<endl;
81         }
82         }
83     return 0;
84 }

 

tarjan算法缩点运算的使用具体事例

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

具体大意:假设有三头公牛a,b,c。a仰慕b,b仰慕c,那么这个c就是剩下的所有公牛的仰慕对象,然后这个题就是让你算出符合条件的公牛一共有多少头。

具体思路:首先,建成一个连通图,通过tarjan算法,然后对强连通子图进行缩点,对缩点后的“新”图来说,求出度为0的缩点中牛的数目。(这个题有一个坑点,就是条件是只要当前的这头牛被剩余的所有的牛仰慕就够了,它本身也可以再去崇拜别的牛,比如说 1  2 3构成一个强连通子图,输出结果应该是3,因为每一头牛都会被剩下的牛所仰慕。))

代码如下:

  1 #include<iostream>
  2 #include<string>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<map>
  7 #include<vector>
  8 #include<stack>
  9 #include<queue>
 10 using namespace std;
 11 #define maxn 50005
 12 int low[maxn],dfn[maxn],vis[maxn],cnt[maxn],color[maxn],out[maxn];//low数组和dfn数组是tarjan算法的基本数组,vis数组是用来判断是否访问过的,cnt数组是用来存 染色后某一种具体颜色的点的个数,color数组是用来染色的,out数组是用来记录缩点之后,某一种颜色对应的出度
 13 int num,ans;
 14 vector<int>wakaka[maxn];
 15 stack<int>q;
 16 void tarjan(int u)
 17 {
 18     vis[u]=1;
 19     q.push(u);
 20     low[u]=dfn[u]=++num;
 21     int len=wakaka[u].size();
 22     for(int i=0; i<len; i++)
 23     {
 24         int v=wakaka[u][i];
 25         if(vis[v]==0)
 26         {
 27             tarjan(v);
 28             low[u]=min(low[u],low[v]);
 29         }
 30         if(vis[v]==1)
 31         {
 32             low[u]=min(low[u],dfn[v]);
 33         }
 34     }
 35     if(low[u]==dfn[u])
 36     {
 37         int top;
 38         ans++;
 39         do
 40         {
 41             top=q.top();
 42             q.pop();
 43             vis[top]=-1;
 44             color[top]=ans//对同一个连通图里的字块进行染色;
 45         }
 46         while(top!=u);
 47     }
 48 }
 49 
 50 int main()
 51 {
 52     int n,m;
 53     while(cin>>n>>m)
 54     {
 55         ans=num=0;
 56         while(!q.empty())q.pop();
 57         for(int i=1; i<=n; i++)
 58         {
 59             wakaka[i].clear();
 60         }
 61         memset(vis,0,sizeof(vis));
 62         memset(cnt,0,sizeof(cnt));
 63         memset(color,0,sizeof(color));
 64         memset(out,0,sizeof(out));
 65         for(int i=1; i<=m; i++)
 66         {
 67             int u,v;
 68             cin>>u>>v;
 69             wakaka[u].push_back(v);
 70         }
 71         for(int i=1; i<=n; i++)
 72         {
 73             if(vis[i]==0)
 74             {
 75                 tarjan(i);
 76             }
 77         }
 78         for(int i=1; i<=n; i++)
 79         {
 80         int t=color[i];
 81             int len=wakaka[i].size();
 82             for(int j=0; j<len; j++)
 83             {
 84            // cout<<i<<" "<<wakaka[i][j]<<endl;
 85                 if(t!=color[wakaka[i][j]])
 86                 {
 87                     out[t]++;//判断染色后某一个强连通子图的出度
 88                 }
 89             }
 90             cnt[t]++;//记录某一个颜色下自快的个数
 91         }
 92         //cout<<ans<<endl<<color[1]<<endl<<color[2]<<endl<<color[3]<<endl;
 93         int x=0,temp;
 94         for(int i=1; i<=ans; i++)
 95         {
 96        // cout<<i<<" "<<out[i]<<" "<<cnt[i]<<endl;
 97             if(out[i]==0)
 98             {
 99                 x++;
100                 temp=cnt[i];
101             }
102             //cout<<temp<<endl;
103         }
104         if(x==1)//只能有一个出度为0的缩点,如果有两个的话是肯定不成立的,打个比方,牛角,两边的端点都是出度为0,但是两边不互相承认对方为最强。
105         {
106             cout<<temp<<endl;
107         }
108         else
109         {
110             cout<<0<<endl;
111         }
112     }
113     return 0;
114 }

 

 

 

原文地址:https://www.cnblogs.com/letlifestop/p/10263004.html