Warm up HDU - 4612
DCC缩点后建立一棵树, 树中的所有边都是桥。 任意连接一条边使桥的数量最少。 那么就是要找出树的最长路径–树的直径。 ans = 桥的数量 - 树的直径
code:
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> using namespace std; const int N = 2e5 + 5, M = 1e6 + 5; struct node{ int u, v, next; } g[M * 2]; int dfn[N], low[N], index[N], depth[N], head[N], front[N], stack[N]; int cnt, opt, tot, cot; bool vis[N], vis_lca[N]; void add(int u, int v){ g[cnt].u = u; g[cnt].v = v; g[cnt].next = head[u]; head[u] = cnt++; return; } void init(int n){ cnt = opt = tot = cot = 0; for(int i = 1; i <= n; i++){ dfn[i] = low[i] = 0; vis[i] = false; index[i] = 0; head[i] = front[i] = -1; } return; } void targan(int u, int in_ridge){ dfn[u] = low[u] = ++opt; vis[u] = true; stack[cot++] = u; for(int i = head[u]; i != -1; i = g[i].next){ int v = g[i].v; if(!dfn[v]){ targan(v, i); low[u] = min(low[u], low[v]); } else if(vis[v] && i != (in_ridge ^ 1)) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]){ int res; tot++; do{ res = stack[--cot]; vis[res] = false; index[res] = tot; } while(u != res); } return; } int dfs_1(int u, int dep){ vis_lca[u] = true; depth[u] = dep; int sno = u; int sco; for(int i = front[u]; i != -1; i = g[i].next){ int v = g[i].v; if(!vis_lca[v]){ sco = dfs_1(v, dep + 1); if(depth[sno] < depth[sco]) sno = sco; } } return sno; } int dfs_2(int u, int dep){ vis_lca[u] = true; int sum = dep; for(int i = front[u]; i != -1; i = g[i].next){ int v = g[i].v; if(!vis_lca[v]) sum = max(sum, dfs_2(v, dep + 1)); } return sum; } int main(){ int n, m, x, y; while(scanf("%d%d", &n, &m), n && m){ init(n); for(int i = 1; i <= m; i++){ scanf("%d%d", &x, &y); add(x, y); add(y, x); } for(int i = 1; i <= n; i++) if(!dfn[i]) targan(i, -1); for(int i = 0; i < cnt; i++){ int u = g[i].u; int v = g[i].v; if(index[u] != index[v]){ g[i].u = index[u]; g[i].v = index[v]; g[i].next = front[index[u]]; front[index[u]] = i; } } for(int i = 1; i <= tot; i++) vis_lca[i] = false; int sno = dfs_1(1, 0); for(int i = 1; i <= tot; i++) vis_lca[i] = false; int sco = dfs_2(sno, 0); printf("%d\n", tot - 1 - sco); } }dalao的代码,从叶子节点开始,求树的直径和值得借鉴
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 2e5 + 5, M = 4e6 + 6; struct E {int v, next;} e[M]; int n, m, u, v, len, ans, dh[N], h[N], dcc_cnt, id[N], num, dfn[N], low[N], d[N]; bool brid[M], vis[N]; void add(int h[], int u, int v) {e[++len].v = v; e[len].next = h[u]; h[u] = len;} void tarjan(int u, int in_edge) { dfn[u] = low[u] = ++num; for (int j = h[u]; j; j = e[j].next) { int v = e[j].v; if (!dfn[v]) { tarjan(v, j); low[u] = min(low[u], low[v]); if (dfn[u] < low[v]) brid[j] = brid[j ^ 1] = true; } else if ((j ^ 1) != in_edge) low[u] = min(low[u], dfn[v]); } } void dfs(int u) { id[u] = dcc_cnt; for (int j = h[u]; j; j = e[j].next) { int v = e[j].v; if (id[v] || brid[j]) continue; dfs(v); } } void dp(int u) { //倒着求树的直径代码 vis[u] = true; for (int j = dh[u]; j; j = e[j].next) { int v = e[j].v; if (vis[v]) continue; dp(v); ans = max(ans, d[u] + d[v] + 1); d[u] = max(d[u], d[v] + 1); } // } int main() { while (scanf("%d%d", &n, &m), n) { memset(h, 0, sizeof(h)); len = num = 1; memset(dh, 0, sizeof(dh)); memset(id, 0, sizeof(id)); memset(dfn, 0, sizeof(dfn)); memset(d, 0, sizeof(d)); memset(brid, false, sizeof(brid)); memset(vis, false, sizeof(vis)); for (int i = 1; i <= m; i++) { scanf("%d%d", &u, &v); add(h, u, v); add(h, v, u); } tarjan(1, 0); dcc_cnt = 0; //缩点 建树 for (int i = 1; i <= n; i++) { if (!id[i]) { dcc_cnt++; dfs(i); } } int tlen = len; for (int j = 2; j <= tlen; j += 2) { u = id[e[j].v], v = id[e[j ^ 1].v]; if (u == v) continue; add(dh, u, v); add(dh, v, u); } ans = 0; dp(1); //求出直径 printf("%d\n", dcc_cnt - 1 - ans); } return 0; }