tarjan縮點(洛谷P3387)

此題解部分借鑑於九野的博客html

題目分析

  • 給定一個 $n$ 個點 $m$ 條邊有向圖,每一個點有一個權值,求一條路徑,使路徑通過的點權值之和最大。你只須要求出這個權值和。數組

  • 容許屢次通過一條邊或者一個點,可是,重複通過的點,權值只計算一次。url

  • 假如沒有後面這條限制的話,那圖必定是一個無環圖。由於有環的話我能夠一直在環上跑,因此答案就沒有一個上界spa

  • 沒有環的話我萌能夠很天然地想到一個 $O(n)$ 的 拓撲$dp$ 作法,先作入度爲 $0$ 的點,更新入度不爲 $0$ 的點,把更新後入度爲 $0$ 的點加入隊列裏,繼續作以前的事情.net

  • 如今考慮有環怎麼作code

  • 有一個貪心的思路是,到環上就先把環上的點都走完,再從環上任意一點出發htm

  • 其實這個環能夠看作一個大點,也就是咱們今天要介紹的主角 $\to$ 縮點blog


tarjan縮點

下面這張圖是從 九野的博客copy 過來的隊列

  • 把能夠互相抵達的點集叫作一個連通份量
  • 最大的那個能夠互相抵達的點點集即爲強連通份量
  • 特別的,單個的點也能夠是強連通份量

好比說 :${ 4, 5 }$ 是一個聯通份量,而 ${4,5,6 } $ 則是一個強連通份量(一個大點)get

tarjan的過程就是經過 dfs 找強連通份量(大點)的過程

對圖dfs一下,遍歷全部未遍歷過的點 ,會獲得一個有向樹,顯然有向樹是沒有環的。(注意搜過的點不會再搜

則能產生環的 <font color = red>只有(指向已經遍歷過的點)的邊</font>

咱們發現 $7 \to 3$ (<font color = red>紅邊</font> / 橫叉邊)這種邊必定不會產生連通份量

而 $6 \to 4$ (<font color = green>綠邊 </font> / 返祖邊)這種邊必定會產生聯通份量

具體來講:具備<font color = red>父子關係</font>的邊必定會產生聯通份量

咱們在dfs的時候須要用一個來保存當前所在路徑上的點<font color = red>(棧中全部點必定是有父子關係的)</font>

咱們用一些數組來表示dfs的過程

int tim, dfn[MAX], low[MAX]

$dfn[i]$ 表示遍歷到節點 $i$ 的時間戳(第幾回遍歷)

$low[i]$ 表示往上能夠到達最先的點

初始化 $dfn[i] = low[i] = ++tim$

咱們能夠根據上面過程的步驟寫出如下代碼

for(int i = head[u]; i; i = nex[i]) {
        if(dfn[to[i]]) {
            if(instack[to[i]]) {
            	if(low[to[i]] < low[u]) {
                	low[u] = low[to[i]];
                }
            }
        } else {
            dfs(to[i]);
            if(low[to[i]] < low[u]) {
            	low[u] = low[to[i]];
            }
        }
    }

假如當前節點 $u$, $dfn[u] == low[u]$ 那就說明棧頂元素一直到節點 $u$ 都屬於一個強聯通份量,感性理解

把棧中元素彈出而且把他們都給標記爲同一種顏色(同一個強連通份量)

if(dfn[u] == low[u]) {
        ++totcol;
        do {
            int v = stk[top];
            col[v] = totcol;
            instack[v] = false;
        } while(stk[top--] != u);
    }

具體實現看代碼

$\color {Deepskyblue} {Code}$

這裏還有一道 tarjan練手好題

原文出處:https://www.cnblogs.com/Lskkkno1/p/11521301.html

相關文章
相關標籤/搜索