1. 程式人生 > >poj 1523 求無向圖所有割點以及刪除割點後連通分量個數 給出詳細演算法思路

poj 1523 求無向圖所有割點以及刪除割點後連通分量個數 給出詳細演算法思路

題意

  • 無向圖找出每個割點,然後求出刪除這個割點所得的連通分量個數
  • 節點編號在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; }