题意是给一个无向图,求添加最少的边使得图中没有桥。
(这样理解:例如一个公路网,有时候某些公路损坏需要检修的时候,为了不影响正常的交通,这个公路网中应该存在另外的路径使得要检修的路所连接的两个点依然联通。如果不存在这样的路径,那么所检修的这条路就叫做无向图中的桥。这个命名很生动。)
求解:先求出双连通分量,将其缩点。最后会得到一棵树。找出度为1的点,设为 T ,那么至少要添加的边数位:(T+1)/2。
怎么证明这个结论?不太懂。这么想吧:首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一
起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是 (T+1)/2 次,把所有点收缩到了一
起。
1 #include<stdio.h>
2 #include<iostream>
3 #include<string.h>
4 #include<queue>
5 #include<map>
6 #include<set>
7 #include<math.h>
8 using namespace std;
9 #define N 5008
10 #define inf 0x7ffffff
11 vector<int> g[N];
12 int cnt, low[N], pre[N], vis[N], deg[N];
13 void dfs(int u, int v) {
14 vis[u] = 1;
15 pre[u] = cnt++, low[u] = pre[u];
16 for (int i = 0; i < g[u].size(); ++i) {
17 if (g[u][i] == v) continue;
18 if (!vis[g[u][i]]) dfs(g[u][i], u);
19 if (low[g[u][i]] < low[u]) low[u] = low[g[u][i]];
20 }
21 vis[u] = 2;
22 }
23 int ok(int x, int y) {
24 for (int i = 0; i < g[x].size(); ++i)
25 if (y == g[x][i]) return 0;
26 return 1;
27 }
28 int main() {
29 int i, j, u, v, n, m, ans;
30 while (scanf("%d %d", &n, &m) != EOF) {
31 for (i = 0; i <= n; ++i) g[i].clear();
32 while (m--) {
33 scanf("%d %d", &u, &v);
34 if (ok(u, v)) {
35 g[u].push_back(v);
36 g[v].push_back(u);
37 }
38 }
39 memset(vis, 0, sizeof (vis));
40 cnt = 0;
41 dfs(1, 1);
42 memset(deg, 0, sizeof (deg));
43 for (i = 1; i <= n; ++i) {
44 for (j = 0; j < g[i].size(); ++j)
45 if (low[i] != low[g[i][j]])
46 deg[low[i]]++;
47 }
48 for (ans = i = 0; i <= n; ++i)
49 if (deg[i] == 1) ++ans;
50 printf("%d\n", (ans + 1) / 2);
51 }
52 return 0;
53 }