hdu 4612 缩点 桥 树的直径
Posted pupil-xj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 4612 缩点 桥 树的直径相关的知识,希望对你有一定的参考价值。
// http://acm.hdu.edu.cn/showproblem.php?pid=4612
// 大致题意: 给n个点和m条边,组成一个无向连通图,问 给我加一条边的权力(可连接任意两点)->让图的桥数量最小,输出此时桥的数量。(2<=N<=200000, 1<=M<=1000000)
// 无向环里面的边没有桥,缩点,因为是连通图,所以缩完点后构成了一棵树,每条树边都是一个桥。要加一条边使得加完后图的桥数最小,结合上述,所以选择连接树直径的两端点。ans = 原先桥数 - 树的直径
树形dp 和 两次dfs(贪心思想)
dp :(代码短 但不能记录路径)
1 void dp(int cur) 2 { 3 vis[cur] = true; //当前结点已访问 4 for(int i = head[cur]; i != -1; i = edge[i].nxt){ 5 int v = edge[i].v; 6 if(vis[v]) continue; //不走回头路 7 dp(v); 8 ans_max = max(ans_max, d[cur] + d[v] + edge[i].w); //更新树的直径(由当前结点两段之和更新) 9 d[cur] = max(d[cur], d[v]+edge[i].w); //更新当前结点所能走的最长路径(保留较长的那边) 10 } 11 }
dfs:(代码长 但是可以记录路径)
1 void dfs(int cur) 2 { 3 for(int i = head[cur]; i != -1; i = edge[i].nxt){ 4 int v = edge[i].v; 5 if(fa[cur] == v) continue; //不走回头路,也可以递归父亲结点省去fa数组空间 6 fa[v] = cur; 7 dis[v] = dis[cur] + edge[i].w; //当前结点最长路径 8 dfs(v); 9 } 10 }第一次dfs(任意点) dfs之后for循环找出最深度的点x 然后再dfs(x)
// hdu 4612
1 /* 2 * @Promlem: 3 * @Time Limit: ms 4 * @Memory Limit: k 5 * @Author: pupil-XJ 6 * @Date: 2019-10-31 23:41:06 7 * @LastEditTime: 2019-11-01 00:57:51 8 */ 9 #include<cstdio> 10 #include<iostream> 11 #include<algorithm> 12 #include<stack> 13 #define read(n) n = read_n() 14 #define rep(i, n) for(int i=0;i!=n;++i) 15 #define rep1(i, n) for(int i=1;i<=n;++i) 16 #pragma comment(linker, "/STACK:102400000,102400000") 17 using namespace std; 18 19 inline int read_n() { 20 char c=getchar();int x=0,f=1; 21 while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} 22 while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();} 23 return x*f; 24 } 25 // ----------------------------------------------------- 26 const int MAXN = 200000+5; 27 const int MAXM = 2000000+5; 28 29 int num; 30 int head[MAXN]; 31 struct node { 32 int v, next; 33 } edge[MAXM]; 34 35 inline void add(int x, int y) { 36 edge[num].v = y; 37 edge[num].next = head[x]; 38 head[x] = num++; 39 } 40 // 上面图结构体 下面树结构体 41 int numt; 42 int headt[MAXN]; 43 struct t { 44 int v, next; 45 } tree[MAXM]; 46 47 inline void addt(int x, int y) {// 加树边 48 tree[numt].v = y; 49 tree[numt].next = headt[x]; 50 headt[x] = numt++; 51 } 52 53 int n, m; 54 int cnt, scc; 55 int dfn[MAXN], low[MAXN], sccno[MAXN]; 56 bool vis[MAXN]; 57 stack<int> st; 58 59 void Tarjan(int cur, int edge_num) {// 缩点 60 dfn[cur] = low[cur] = ++cnt; 61 vis[cur] = true; 62 st.push(cur); 63 for(int i = head[cur]; i != -1; i = edge[i].next) { 64 if(edge_num != -1 && i == (edge_num^1)) continue; 65 int v = edge[i].v; 66 if(!dfn[v]) { 67 Tarjan(v, i); 68 low[cur] = min(low[cur], low[v]); 69 } 70 else if(vis[v]) low[cur] = min(low[cur], dfn[v]); 71 } 72 if(dfn[cur] == low[cur]) { 73 ++scc; 74 int v; 75 do { 76 v = st.top(); 77 st.pop(); 78 vis[v] = false; 79 sccno[v] = scc; 80 } while(v != cur); 81 } 82 } 83 84 int ans; 85 int dis[MAXN]; 86 87 void build() {// 建树 88 numt = 0; 89 rep1(u, n) { 90 for(int i = head[u]; i != -1; i = edge[i].next) { 91 int v = edge[i].v; 92 if(sccno[u] != sccno[v]) { 93 addt(sccno[u], sccno[v]); 94 addt(sccno[v], sccno[u]); 95 } 96 } 97 } 98 } 99 100 void dp(int cur) {// 求直径 101 vis[cur] = true; 102 for(int i = headt[cur]; i != -1; i = tree[i].next) { 103 int v = tree[i].v; 104 if(vis[v]) continue; 105 dp(v); 106 ans = max(ans, dis[cur] + dis[v] + 1); 107 dis[cur] = max(dis[cur], dis[v] + 1); 108 } 109 } 110 111 void solve() { 112 cnt = scc = 0; 113 rep1(i, n) dfn[i] = 0, vis[i] = false, headt[i] = -1; 114 Tarjan(1, -1); 115 build(); 116 rep1(i, scc) vis[i] = false, dis[i] = 0; 117 ans = 0; 118 dp(1); 119 cout << scc-1-ans << " "; 120 } 121 122 int main() { 123 while(scanf("%d%d", &n, &m) == 2 && n) { 124 num = 0; 125 rep1(i, n) head[i] = -1; 126 int x, y; 127 rep(i, m) { 128 read(x); read(y); 129 add(x, y); add(y, x); 130 } 131 solve(); 132 } 133 return 0; 134 }
以上是关于hdu 4612 缩点 桥 树的直径的主要内容,如果未能解决你的问题,请参考以下文章
HDU 4612 Warm up(双连通分量缩点+求树的直径)