【算法學習】 tarjan算法以及圖論的一些應用(強連通分量/割點/橋/縮點)

學習了tarjan算法,覺得這個算法真的是挺強大的,然而蒟蒻並不會用 ,先學習着寫一篇博客作爲記錄。

學習tarjan的點覺得主要在於兩個數組的理解—— dfn,lowdfn,low數組
dfndfn數組,用於記錄dfsdfs序,也就是這個點最早什麼時候被dfs搜索到。
lowlow數組,用於記錄其及其子樹中的點能回溯到的點的最小dfsdfs

這兩個數組引出了很多很多應用

tarjan模板

const int maxn = 1e5+5;
struct Edge{
	int u,v,nxt;
}edge[maxn];
int head[maxn],tot;
inline void addedge(int u,int v){
	edge[++tot] = {u,v,head[u]};
	head[u] = tot;
}
bool vis[maxn];
int dfn[maxn],sta[maxn],low[maxn],stalen,num,color[maxn],dfnnum;
void tarjan(int u){
	dfn[u] = low[u] = ++dfnnum;
	sta[stalen++] = u; vis[u] = true;
	for(int i = head[u]; ~i; i = edge[i].nxt){
		Edge &e = edge[i];
		if(!dfn[e.v]){
			tarjan(e.v);
			low[u] = min(low[u],low[e.v]);
		}else if(vis[e.v]) low[u] = min(low[u],vis[e.v]);
	}
}

應用

1.求強連通分量

  1. 強連通分量的定義:
    有向圖強連通分量:在有向圖GG中,如果兩個頂點Vi,VjV_i,V_j間(Vi!=VjV_i!=V_j)有一條從ViV_iVjV_j 的有向路徑,同時還有一條從ViV_iVjV_j 的有向路徑,則稱兩個頂點強連通。如果有向圖GG的每兩個頂點都強連通,稱GG是一個強連通圖。有向圖的極大強連通子圖,稱爲強連通分量。 ——百度百科

例如在這裏插入圖片描述
如何求一個有向圖中的強連通分量呢?
dfn==lowdfn == low
原因還是要從dfndfnlowlow的含義來分析,上面我們提到:

  • dfndfn數組,用於記錄dfsdfs序,也就是這個點最早什麼時候被dfs搜索到。
  • lowlow數組,用於記錄其及其子樹中的點能回溯到的點的最小dfsdfs
  • 這說明了uu點及uu點之下的所有子節點沒有邊是指向u的祖先的了,即我們之前說的uu點與它的子孫節點構成了一個最大的強連通圖即強連通分量
    然後呢,不斷的出棧頂元素直到 u == cur,所有被出棧的元素都屬於同一個強連通分量。

代碼:

const int maxn = 1e5+5;
struct Edge{
	int u,v,nxt;
}edge[maxn];
int head[maxn],tot;
inline void addedge(int u,int v){
	edge[++tot] = {u,v,head[u]};
	head[u] = tot;
}
bool vis[maxn];
int dfn[maxn],sta[maxn],low[maxn],stalen,num,color[maxn],dfnnum;
void tarjan(int u){
	dfn[u] = low[u] = ++dfnnum;
	sta[stalen++] = u; vis[u] = true;
	for(int i = head[u]; ~i; i = edge[i].nxt){
		Edge &e = edge[i];
		if(!dfn[e.v]){
			tarjan(e.v);
			low[u] = min(low[u],low[e.v]);
		}else if(vis[e.v]) low[u] = min(low[u],vis[e.v]);
	}
	if(dfn[u]==low[u]){
		++num;	int cur;
		do{
			cur = sta[--stalen];
			vis[cur] = 0;
			color[cur] = num;	// 染色,同一個顏色的爲一個強連通分量
		}while(u!=cur);
	}
}

我們可以來做一個簡單的模擬即可明白具體過程,以及算法的正確性
在這裏插入圖片描述
模板題練手
P2341 [HAOI2006]受歡迎的牛|【模板】強連通分量


2. 縮點

簡單來說,一個強連通分量內的點都可以互相可達,舉信息傳遞的例子來說,如果任意選擇一個強連通分量內的點作爲信息源,在這個強連通分量內的其他點都可以收到信息,於是就引出了一個縮點的概念(縮點在很多圖論題目中應用很廣泛,可以使得一些有向有環圖變成有向無環圖,於是就可以用到拓撲排序等等之類的在DAG上可以用到的算法)

因此縮點是強連通分量的一個應用 (我認爲

模板題:P3387 【模板】縮點
然後分析一下這道題目爲什麼要用到縮點呢,看一下題意

給定一個n個點m條邊有向圖,每個點有一個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。
允許多次經過一條邊或者一個點,但是,重複經過的點,權值只計算一次。

對於一個強聯通分量來說,如果可以走進這個強連通分量,那麼這個分量裏的點都要走一遍(因爲這樣是最優的,那麼我們就可以把整個連通分量縮小成一個點,並且這個點的點權爲所有點的點權之和)

然後把這些點再重新建邊,跑一遍記搜或者dp(DP好像需要用拓撲序)就可以了


3.割點

在無向連通圖中,如果將其中一個點以及所有連接該點的邊去掉,圖就不再連通,那麼這個點就叫做割點(cut vertex / articulation point)。(割點一般對於無向圖來說)

找割點
low[e.v]dfn[u]low[e.v] \geq dfn[u],此時uu就爲割點了


總的來說…這些都是一些基礎知識,對於題目來說要靈活使用才能做出題目

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章