標籤:
這幾天一直在做強連通,現在總結一小下
1.定義
在一個有向圖中,如果任意的兩個點都是相互可達的,就說這個圖是強連通的,有向圖的極大強連通子圖,稱為強連通分量
2.求法
學的是白書上的tarjan演算法
用到了DFS的時間戳記
假設一個強連通分量C,其中的第一個點是 P,那麼DFS下去,就一定能夠找到一個K點,返回P點,這條DFS路徑上的點就處於這個強連通分量C中
假如現在發現節點v,同時發現節點v最遠只能夠到達節點u,那麼節點u就是這個強連通分量最先被發現的節點
這樣就轉化成了求一個點u最遠能夠到達的的祖先的d值
所以要定義一個low[]數組為節點u最早能夠追溯到的祖先的pre[v]的值,pre[v]為節點v的時間戳記
void dfs(int u){ low[u] = pre[u] = ++dfs_clock; S.push(u); for(int i = first[u];~i;i = e[i].next){ int v = e[i].v; if(!pre[v]){//子節點還沒有被搜過,則搜下去,回溯回來更新low值 dfs(v); low[u] = min(low[u],low[v]); } else if(!sc[v]) low[u] = min(low[u],pre[v]);//如果當前節點不屬於其他scc,且已經被訪問過,說明它是當前節點的祖先節點,直接用它的low更新 } if(pre[u] == low[u]){ scnt++; for(;;){ int x = S.top();S.pop(); sc[x] = scnt; // scn[scnt]++;這裡可以統計一個連通分量裡面有多少個點,或者當點有點權的時候,算出這個連通分量的權值 if(x == u) break; } }}void find_scc(){ while(!S.empty()) S.pop(); scnt = dfs_clock = 0; memset(low,0,sizeof(low));memset(pre,0,sizeof(pre)); memset(sc,0,sizeof(sc));memset(scn,0,sizeof(scn)); for(int i = 1;i <= n;i++) if(!pre[i]) dfs(i);}
3.現在做到的一些題目
現在做到的題目還是挺有限的,把自己做的總結下---以後遇到再補
(1)判斷是否是一個強連通圖
1)直接給一個有向圖,判斷是不是強連通的
hdu 1269
2) 附加一點限制的
加一個限制這個強連通分量裡面的節點個數大於1的,tarjan演算法裡面直接再統計一下個數
poj 3180
( 2 )和入度出度有關的
1)加入幾條邊使得整個圖強連通
白書上強連通那裡的第一道例題
先tarjan,找出所有的強連通分量
再縮點,
再統計圖的入度,出度,
另入度為0的有c1個,出度為0的有c2個,max(c1,c2)就是要求的
la 4287
hdu 2767
hdu 3836
這三道都是一樣的
2)從哪些點出發,使得所有的點都能夠走到
縮點,縮點後所有入度為0的就是要求的
hdu 1827 找入度為0的連通塊,還附加了一個權值
poj 1236 max(c1,c2),和輸出入度為0的連通分量的個數
poj 2553 統計出度為0的塊
poj 2186 找有幾個點是別的點都能夠到達的,統計出度為0的塊的個數c,
注意c > 1的情況,出度為0的塊裡面的點是相互不能夠到達的,應該輸出0
poj 2375 將圖轉化以後就是求max(c1,c2)
(3)和別的結合的
一般要用到縮點,縮完點之後形成一個有向非循環圖,構成一個DAG,
就可以dp,dfs,spfa
uva 11324
先求出所有強連通,再縮點,縮點完之後建立一個新的圖
建圖的辦法是,掃一遍原來所有的邊,如果發現這條邊的兩個端點u,v處在不同的連通分量中,就在sc[u],sc[v]之間連一條邊
則轉化成求DAG上權最大的路徑
還可以有另一種辦法
加一個源點進去,將這個源點與縮點完之後所有入度為0的點連一條邊
再spfa求出權最大的路徑
poj 3160和這題一樣
poj 2762 縮點之後拓撲排序
poj 3114 縮點之後dijkstra,用floyd會t掉
poj 3592 圖轉化一下以後,spfa
現在就做了這些----
看的這兩篇部落格做得題目---
http://blog.csdn.net/accelerator_/article/details/40349411
http://blog.csdn.net/u013013910/article/category/2509879
Book---強連通分量