题目:
一棵树,边有流量。让你找一个点u到所有叶子结点路径上流量和最大。
做法:
换根dp。
首先选定一个非叶子结点为根,一遍得到子树到该子树下叶子结点的流量。 (v是u的儿子结点,w是边的流量)。 表示以为树根点,到所有叶子结点路径上流量和。显然此时我们以此为基础再一遍将根换到儿子结点,求得所有值。
考虑根从换到的影响。
首先子树向下的流量加上去,然后截断向的流量,并加上从到的流量:
最后
PS:代码中为了处理方便,叶子结点的流量flow被设成了边的流量,在换根dp时需要减回来。
PPS:感谢评论区指出错误:不能默认用1为根,因为1可能是叶子结点换根时会少算通向1的流量。所以我们要特判和的情况,当时,树上必定存在非叶子结点,从开始就不会出现问题了。
代码:
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false), cin.tie(0) #define debug(a) cout << #a ": " << a << endl using namespace std; typedef long long ll; const int N = 2e5 + 7; const int inf = 0x3f3f3f3f; int n; ll flow[N], dp[N]; vector<pair<int,int> > g[N]; void dfs(int u, int p){ if (g[u].size() == 1 && u != p) return; flow[u] = 0; for (int i = 0; i < g[u].size(); ++i){ int v = g[u][i].first, w = g[u][i].second; if (v == p) continue; flow[v] = w; dfs(v, u); flow[u] += min(flow[v], 1ll*w); } } void dfs1(int u, int p){ for (int i = 0; i < g[u].size(); ++i){ int v = g[u][i].first, w = g[u][i].second; if (v == p) continue; dp[v] = flow[v]+min(1ll*w, dp[u]-min(flow[v], 1ll*w)); if (g[v].size() == 1) dp[v] -= flow[v]; dfs1(v, u); } } int main(void){ IOS; int T; cin >> T; while (T--){ cin >> n; for (int i = 1; i <= n; ++i) g[i].clear(); for (int i = 0; i < n-1; ++i){ int u, v, w; cin >> u >> v >> w; g[u].push_back(make_pair(v, w)); g[v].push_back(make_pair(u, w)); } if (n == 1){ cout << 0 << endl; continue; }else if (n == 2){ cout << g[1][0].second << endl; continue; } int rt; for (rt = 1; rt <= n && g[rt].size() == 1; ++rt); dfs(rt, rt); dp[rt] = flow[rt]; dfs1(rt, rt); ll ans = 0; for (int i = 1; i <= n; ++i){ ans = max(ans, dp[i]); } cout << ans << endl; } return 0; }