標籤:style blog http color io os ar for sp
題目連結
題意:問要添加幾條邊才能使所給無向圖圖變成邊雙連通圖。
思路:一個有橋的連通圖,如何把它通過加邊變成邊雙連通圖?方法為首先求出所有的橋,然後刪除這些橋邊,剩下的每個連通塊都是一個雙連通子圖。把每個雙連通子圖收縮為一個頂點,再把橋邊加回來,最後的這個圖一定是一棵樹,邊連通度為1。
統計出樹中度為1的節點的個數,即為分葉節點的個數,記為leaf。則至少在樹上添加(leaf+1)/2條邊,就能使樹達到邊二連通,所以至少添加的邊數就是(leaf+1)/2。具體方法為,首先把兩個最近公用祖先最遠的兩個分葉節點之間串連一條邊,這樣可以把這兩個點到祖先的路徑上所有點收縮到一起,因為一個形成的環一定是雙連通的。然後再找兩個最近公用祖先最遠的兩個分葉節點,這樣一對一對找完,恰好是(leaf+1)/2次,把所有點收縮到了一起。
代碼:
#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <algorithm>using namespace std;const int MAXN = 5005;vector<int> g[MAXN];int pre[MAXN], low[MAXN], dg[MAXN], dfs_clock;bool vis[MAXN], map[MAXN][MAXN];int n, r;void tarjan(int u, int fa) { pre[u] = low[u] = ++dfs_clock; vis[u] = 1; for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (v == fa) continue; if (!pre[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); } else if (vis[v]) { low[u] = min(low[u], pre[v]); } }}void find_ebc() { memset(pre, 0, sizeof(pre)); memset(low, 0, sizeof(low)); memset(vis, 0, sizeof(vis)); dfs_clock = 0; for (int i = 1; i <= n; i++) if (!pre[i]) tarjan(i, -1);}int main() { while (scanf("%d%d", &n, &r) != EOF) { for (int i = 1; i <= n; i++) g[i].clear(); memset(map, 0, sizeof(map)); int u, v; for (int i = 0; i < r; i++) { scanf("%d%d", &u, &v); if (!map[u][v]) { g[u].push_back(v); g[v].push_back(u); map[u][v] = map[v][u] = 1; } } find_ebc(); memset(dg, 0, sizeof(dg)); for (int u = 1; u <= n; u++) { for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (low[u] != low[v]) dg[low[u]]++; } } int cnt = 0; for (int i = 1; i <= n; i++) if (dg[i] == 1) cnt++; printf("%d\n", (cnt + 1) / 2); } return 0;}
POJ3352-Road Construction(邊連通分量)