题目描述
给定n个点, n-1条边, 边上有权值代表流量, 与网络流最大流问题相似
问以任意一点为起点, 全图的最大流量是多少
输入包括多组数据
样例
输入样例:
1
5
1 2 11
1 4 13
3 4 5
4 5 10
输出样例:
26
样例分析
5个点, 4条边。当以4为起点时, 得到最大流量为26。
思路
做两次扫描
- 任意选取一个节点当做根节点, 做一次树形DP,至下往上进行状态转移,更新D数组的值,D数组存的是D[x] 是 x的子树的最大流量
- 以刚才的根节点出发, 扫描所有节点, 自定往下进行更新F数组, F数组存的是 F[x] 以x为根节点的整棵树的最大总流量。
用deg数组存节点的度, 以便判断叶子结点, 是第一次搜索时, 状态转移是如下代码所示,当y为叶子结点时, y的子树的最大流就是 d[x] + w[x][y] 这里的w[x][y]表示x 到 y 的流量,代码中用w[i]表示, 如果不是叶子结点, 流量就等于 y的子树的最大流量 和 x 到 y 的流量 取最小值。
if(deg[y] == 1) d[x] += w[i];
else d[x] += min(d[y], w[i]);
在进行F数组更新时也是类似的做法,由于是自上往下更新, 所以和上面会在更新存值时有一点不同,这里也是X -->> Y 如果X是叶子结点, F[Y] = d[y] + w[x][y],不然的话, 就是F[x]的总流量减去x流向y的最大流,这就是x流向另外节点的总流量,再在这个总流量,和w[y][x]取一个最小值,就是y流向x的最大流,再加上y流向其他节点的总流量,就是以y为根节点的这棵树的最大总流量
if(deg[x] == 1) f[y] = d[y] + w[i];
else f[y] = d[y] + min(f[x] - min(d[y], w[i]), w[i]);
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2e5 + 10, M = N * 2;
int e[M], ne[M], h[M], w[M], len;
int f[N], d[N], n, deg[N];
bool vis[N];
void add(int a, int b, int c){
e[len] = b;
w[len] = c;
ne[len] = h[a];
h[a] = len++;
}
void dfs_d(int x){
d[x] = 0;
vis[x] = true;
for(int i = h[x]; ~i; i = ne[i]){
int y = e[i];
if(vis[y]) continue;
dfs_d(y);
if(deg[y] == 1) d[x] += w[i];
else d[x] += min(d[y], w[i]);
}
}
void dfs_f(int x){
vis[x] = true;
for(int i = h[x]; ~i; i = ne[i]){
int y = e[i];
if(vis[y]) continue;
if(deg[x] == 1) f[y] = d[y] + w[i];
else f[y] = d[y] + min(f[x] - min(d[y], w[i]), w[i]);
dfs_f(y);
}
}
int main(){
int t;
cin >> t;
while(t--){
cin >> n;
len = 0;
memset(vis, false, sizeof vis);
memset(h, -1, sizeof h);
memset(deg, 0, sizeof deg);
memset(f, 0, sizeof f);
for(int i = 1; i < n; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
deg[a]++, deg[b]++;
}
dfs_d(1);
f[1] = d[1];
memset(vis, false, sizeof vis);
dfs_f(1);
int ans = 0;
for(int i = 1; i <= n; i++) ans = max(ans, f[i]);
cout << ans << endl;
}
return 0;
}