poj 1523 求無向圖所有割點以及刪除割點後連通分量個數 給出詳細演算法思路
阿新 • • 發佈:2018-11-11
題意
- 無向圖找出每個割點,然後求出刪除這個割點所得的連通分量個數
- 節點編號在1-1000,但沒說按順序給出
思路
- 無向圖求所有割點是一類經典問題,這篇blog就以這題為例簡單介紹一下求解的演算法思路
- 我們希望在O(n+m)的時間裡求出所有的割點
- 首先,總體思路是對圖進行dfs操作,dfs的過程其實對應了一棵dfs搜尋樹,而我們就利用這棵搜尋樹的獨特性質,來解決求割點的問題
- dfs過程是,當我們搜尋到節點u時,會遍歷和u有邊連結的所有節點v,這裡有兩種情況,一是v已經被搜尋過了,二是v還沒有被搜尋過
- 對於第二種情況,我們自然會繼續dfs(v)
- 而對於第一種情況,在無向圖中,存在一個很好的性質。即v節點一定是dfs樹中,u的一個祖先節點(或者u本身,這個情況可以通過把父親節點id作為引數傳給dfs避免掉,詳細參見程式碼)。這個性質通過畫個事例,然後反證法,很容易證明。這種情況中的邊(u, v),我們稱之為反向邊
- 我們根據這個性質,可以得到一個引理1:若u是dfs樹中的非根節點,則u是割點,當且僅當,dfs樹中存在一個u的孩子節點v,以v為根的子樹中,任意節點都沒有連到u的祖先節點的反向邊
- 而當u是根節點時,則u是割點的充要條件是,在dfs樹中u的孩子節點數超過1
- 這個證明根據之前我們說的性質,和割點的定義不難推出
- 具體實現的時候,我們通過維護兩個陣列pre和low來實現
- pre記錄一個時間戳,即pre[u]記錄u是第幾個被訪問的節點
- low[u]記錄以u為子樹的節點中,反向邊連到最早被訪問的節點的pre值
- 具體到這個題裡,要求刪除節點u後的聯通分量個數,只要統計一下滿足引理1的節點v的個數即可知道
- 注意:節點編號不是連續排布以及圖可能不連通的問題
實現
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <set>
#include <cstring>
using namespace std;
const int maxn = 1e3+5;
map<int, set<int> > g;
map<int, int> pre;
map<int, int> low;
int count = 0;
map<int, int> cutNum;
int dfs(int u, int fa){
low[u] = pre[u] = ++count;
int child = 0;
for (set<int>::iterator it = g[u].begin(); it != g[u].end(); it++){
int v = *it;
if (fa == v){
continue;
}
if (!pre[v]){
child++;
low[u] = min(low[u], dfs(v, u));
if (low[v] >= pre[u]){
cutNum[u]++;
}
}
else{
low[u] = min(low[u], pre[v]);
}
}
if (fa == -1 && child == 1){
cutNum.erase(cutNum.lower_bound(u));
}
else if (fa != -1 && cutNum.find(u) != cutNum.end()){
cutNum[u]++;
}
return low[u];
}
int main(){
for (int t=1;true;t++){
int u, v;
g.clear();
pre.clear();
cutNum.clear();
low.clear();
count = 0;
while (scanf("%d", &u) != EOF && u){
scanf("%d", &v);
g[u].insert(v);
g[v].insert(u);
}
if (!g.size()){
break;
}
for (map<int, set<int> >::iterator it = g.begin(); it != g.end(); it++){
int u = it->first;
if (!pre[u]){
dfs(u, -1);
}
}
printf("Network #%d\n", t);
if (!cutNum.size()){
puts(" No SPF nodes");
}
for (map<int, int>::iterator it = cutNum.begin(); it!=cutNum.end();it++){
int u = it->first;
int num = it->second;
printf(" SPF node %d leaves %d subnets\n", u, num);
}
puts("");
}
return 0;
}