此題解部分借鑑於九野的博客html
給定一個 $n$ 個點 $m$ 條邊有向圖,每一個點有一個權值,求一條路徑,使路徑通過的點權值之和最大。你只須要求出這個權值和。數組
容許屢次通過一條邊或者一個點,可是,重複通過的點,權值只計算一次。url
假如沒有後面這條限制的話,那圖必定是一個無環圖。由於有環的話我能夠一直在環上跑,因此答案就沒有一個上界spa
沒有環的話我萌能夠很天然地想到一個 $O(n)$ 的 拓撲$dp$ 作法,先作入度爲 $0$ 的點,更新入度不爲 $0$ 的點,把更新後入度爲 $0$ 的點加入隊列裏,繼續作以前的事情.net
如今考慮有環怎麼作code
有一個貪心的思路是,到環上就先把環上的點都走完,再從環上任意一點出發htm
其實這個環能夠看作一個大點,也就是咱們今天要介紹的主角 $\to$ 縮點blog
下面這張圖是從 九野的博客 那 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); }
具體實現看代碼
這裏還有一道 tarjan練手好題
原文出處:https://www.cnblogs.com/Lskkkno1/p/11521301.html