RoNgDaZhOnG

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

强连通分量求解算法有Kosaraju和Tarjan_SCC,Kosaraju需要进行两遍dfs,而Tarjan只需一遍dfs,
可见Tarjan的运行效率比Kosaraju高,常数小。
而且以后求图的双联通分量、割、桥之类的还需要用到Tarjan,所以学习Tarjan的价值更大一些。

Tarjan算法的思想:
dfn[v]表示v节点在dfs中是第几个被搜索到得(时间戳),low[v]表示从v节点出发dfs过程中v下方节点(开始时间大于dfn[v],且由v可到达的节点) 所能到达的最早的节点的开始时间。初始时low[v]=dfn[v]=dfs_timer++

Tarjan算法基于dfs,当搜索一个节点v时:
    将v加入栈中,将dfn[v]和low[v]全部置为dfs的搜索时间戳
    搜索所有v能到达的节点u:
        如果u已经被搜索过了 并且 并未求出它在其他强连通分量内,使low[v]=min(low[v],dfn[u]);★★★★★
        如果u未被搜索过,则搜索u,并在搜索完u后使:low[v]=min(low[v],low[u]);
    搜索完所有u后,如果dfn[v]==low[v]则表示 当前栈顶一直到v的节点在一个强连通分量中。

这个算法巧妙之处在此,如果节点v为强连通分量中的第一个被搜索到的节点,则必然有dfn[v]==low[v],因为★★★★★标记处的步骤。

蛮难理解的,看了好久。下面附一段tarjan:

void dfs(int v){
     dfn[v]=low[v]=++dfs_time;
     st.push(v);
     for(int i=0;i<love[v].size();i++){
             if(!dfn[love[v][i]]){
                                  dfs(love[v][i]);
                                  low[v]=min(low[v],low[love[v][i]]);
             }
             else{
                  if(!scc[love[v][i]]) low[v]=min(low[v],dfn[love[v][i]]);
                  //这句if的判断条件是:love[v][i]这个点是否在栈内
                  //即该点不在已求出的其他强连通分量内 
             }
     }
     
     if(dfn[v]==low[v]){
                        scc_cnt++;
                        while(1){
                                 int u=st.top();st.pop();
                                 scc[u]=scc_cnt;
                                 if(u==v) break;
                        }
     }
}   

void Tarjan(){
     for(int i=1;i<=n;i++){
             if(scc[i]==0) dfs(i);
     }
}

直接执行Tarjan()即可。

然后就去做了下codevs2822,写了好久最后被逼无奈用了并查集,心好累。
下面是标程:

 1 #define MAXN 100010
 2 #include<iostream>
 3 #include<stack>
 4 #include<algorithm>
 5 #include<vector>
 6 #include<cstdio>
 7 #include<cstdlib>
 8 using namespace std;
 9 
10 int n,m;
11 stack <int> st;
12 vector <int> love[MAXN];
13 vector <int> scc_map[MAXN];
14 int dfs_time,scc_cnt,lover_num;
15 int scc[MAXN],dfn[MAXN],low[MAXN],inner[MAXN];
16 int a[MAXN],b[MAXN],ans[MAXN];
17 int par[MAXN];
18 
19 void dfs(int v){
20      dfn[v]=low[v]=++dfs_time;
21      st.push(v);
22      for(int i=0;i<love[v].size();i++){
23              if(!dfn[love[v][i]]){
24                                   dfs(love[v][i]);
25                                   low[v]=min(low[v],low[love[v][i]]);
26              }
27              else{
28                   if(!scc[love[v][i]]) low[v]=min(low[v],dfn[love[v][i]]);
29                   //这句if的判断条件是:love[v][i]这个点是否在栈内
30                   //即该点不在已求出的其他强连通分量内 
31              }
32      }
33      
34      if(dfn[v]==low[v]){
35                         scc_cnt++;
36                         while(1){
37                                  int u=st.top();st.pop();
38                                  scc[u]=scc_cnt;
39                                  if(u==v) break;
40                         }
41      }
42 }   
43 
44 void Tarjan(){
45      for(int i=1;i<=n;i++){
46              if(scc[i]==0) dfs(i);
47      }
48 }
49 
50 int get_par(int a){
51     if(par[a]!=a) par[a]=get_par(par[a]);
52     return par[a];
53 }
54 void merge(int a,int b){
55      if(get_par(a)==get_par(b)) return;
56      par[get_par(a)]=get_par(b);
57 } 
58 //打到这要吐血了,还要用并查集
59  
60 int get_lover(){
61     for(int i=1;i<=scc_cnt;i++) par[i]=i;
62     for(int i=0;i<m;i++){
63             if(scc[a[i]]!=scc[b[i]]){inner[scc[a[i]]]++;}
64             merge(scc[a[i]],scc[b[i]]);
65             //如果一个天使被所有人所爱,它必定不爱任何一个人,入度为0
66     }
67     for(int i=2;i<=scc_cnt;i++) if(get_par(i)!=get_par(1)) return -1;
68     for(int i=1;i<=scc_cnt;i++){
69             if((!inner[i]) && scc_map[i].size()>1) return i;
70     }
71     return -1;
72 }
73     
74      
75 int main(){
76     scanf("%d%d",&n,&m);
77     for(int i=0;i<m;i++){
78             scanf("%d %d",&a[i],&b[i]);
79             love[a[i]].push_back(b[i]);
80     }
81     
82     Tarjan();
83     
84     for(int i=1;i<=n;i++){
85             if(scc_map[scc[i]].size()==1) lover_num++;
86             scc_map[scc[i]].push_back(i);
87     }
88     printf("%d\n",lover_num);
89     
90     int j=0,boss=get_lover();
91     if(boss==-1){cout<<-1<<endl;}
92     else{
93          for(int i=0;i<scc_map[boss].size();i++)  ans[j++]=scc_map[boss][i];
94          sort(ans,ans+j);
95          for(int i=0;i<j-1;i++) printf("%d ",ans[i]);
96          printf("%d\n",ans[j-1]);
97     }
98     return 0;
99 }

 

posted on 2017-03-12 09:29  学无止境-1980  阅读(98)  评论(0编辑  收藏  举报