初學強連通演算法--Tarjan
Tarjan演算法的詳細解釋見上面兩個部落格。。。。。
我們討論一下Tarjan演算法能夠幹一些什麼: 既然我們知道,Tarjan演算法相當於在一個有向圖中找有向環,那麼我們Tarjan演算法最直接的能力就是縮點辣!縮點基於一種染色實現,我們在Dfs的過程中,嘗試把屬於同一個強連通分量的點都染成一個顏色,那麼同一個顏色的點,就相當於一個點。比如剛才的例項圖中縮點之後就可以變成這樣: 將一個有向帶環圖變成了一個有向無環圖(DAG圖)。很多演算法要基於有向無環圖才能進行的演算法就需要使用Tarjan演算法實現染色縮點,建一個DAG圖然後再進行演算法處理。在這種場合,Tarjan演算法就有了很大的用武之地辣!
那麼這個時候 ,我們再引入一個數組color【i】表示節點i的顏色,再引入一個數組stack【】實現一個棧,然後在Dfs過程中每一次遇到點都將點入棧,在每一次遇到關鍵點的時候將棧內元素彈出,一直彈到棧頂元素是關鍵點的時候為止,對這些彈出來的元素進行染色即可。
void Tarjan(int u)//此程式碼僅供參考 { vis[u]=1; low[u]=dfn[u]=cnt++; stack[++tt]=u; for(int i=0;i<mp[u].size();i++) { int v=mp[u][i]; if(vis[v]==0)Tarjan(v); if(vis[v]==1)low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { sig++; do { low[stack[tt]]=sig; color[stack[tt]]=sig; vis[stack[tt]]=-1; } while(stack[tt--]!=u); } }
上一道例題:
The Bottom of a Graph Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 12220 Accepted: 5012 Description
We will use the following (standard) definitions from graph theory. Let V be a nonempty and finite set, its elements being called vertices (or nodes). Let E be a subset of the Cartesian product V×V, its elements being called edges. Then G=(V,E) is called a directed graph. Let n be a positive integer, and let p=(e1,…,en) be a sequence of length n of edges ei∈E such that ei=(vi,vi+1) for a sequence of vertices (v1,…,vn+1). Then p is called a path from vertex v1 to vertex vn+1 in G and we say that vn+1 is reachable from v1, writing (v1→vn+1). Here are some new definitions. A node v in a graph G=(V,E) is called a sink, if for every node w in G that is reachable from v, v is also reachable from w. The bottom of a graph is the subset of all nodes that are sinks, i.e., bottom(G)={v∈V|∀w∈V:(v→w)⇒(w→v)}. You have to calculate the bottom of certain graphs. Input
The input contains several test cases, each of which corresponds to a directed graph G. Each test case starts with an integer number v, denoting the number of vertices of G=(V,E), where the vertices will be identified by the integer numbers in the set V={1,…,v}. You may assume that 1<=v<=5000. That is followed by a non-negative integer e and, thereafter, e pairs of vertex identifiers v1,w1,…,ve,we with the meaning that (vi,wi)∈E. There are no edges other than specified by these pairs. The last test case is followed by a zero. Output
For each test case output the bottom of the specified graph on a single line. To this end, print the numbers of all nodes that are sinks in sorted order separated by a single space character. If the bottom is empty, print an empty line. Sample Input
3 3 1 3 2 3 3 1 2 1 1 2 0 Sample Output
1 3 2
題目大意:給你一堆點,一堆邊,讓你找到縮點之後出度為0的節點, 然後將節點編號從小到大排序輸出。
思路:Tarjan,縮點染色,判斷出度為0的強連通分量,將整個集合排序,輸出即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e6 + 5;
vector<int>ve[N];
int ans[N];
int deg[N];
int color[N],vis[N],dfn[N],low[N];
int n,m,t,cnt,sig;
//模擬棧
int sta[N];
void init()
{
for(int i = 1;i <= n;++i) ve[i].clear();
memset(deg,0,sizeof(deg));
memset(color,0,sizeof(color));
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
}
void Tarjan(int u)
{
vis[u] = 1;
dfn[u] = low[u] = cnt++;
sta[++t] = u;
int len = ve[u].size();
for(int i = 0;i < len;++i){
int v = ve[u][i];
if(vis[v] == 0) Tarjan(v);
if(vis[v] == 1) low[u] = min(low[u],low[v]);
}
if(dfn[u] == low[u]){
sig++;
do{
color[sta[t]] = sig;
vis[sta[t]] = 1;
}while(sta[t--] != u);
}
}
void solve()
{
t = -1;cnt = 1;sig = 0;
for(int i = 1;i <= n;++i){
if(vis[i] == 0){
Tarjan(i);
}
}
for(int i = 1;i <= n;++i){
int len = ve[i].size();
for(int j = 0;j < len;++j){
int v = ve[i][j];
if(color[i] != color[v]){
deg[color[i]]++;
}
}
}
int pp = 0;
for(int i = 1;i <= sig;++i){
if(deg[i] > 0) continue;
for(int j = 1;j <= n;++j){
if(color[j] == i){
ans[pp++] = j;
}
}
}
sort(ans,ans + pp);
for(int i = 0;i < pp;++i){
if(i == pp - 1) printf("%d\n",ans[i]);
else printf("%d ",ans[i]);
}
}
int main()
{
while(~scanf("%d",&n))
{
if(n == 0) break;
scanf("%d",&m);
init();
for(int i = 0;i < m;++i){
int x,y;
scanf("%d %d",&x,&y);
ve[x].pb(y);
}
solve();
}
return 0;
}