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 }