题目链接: poj 3585
借鉴自《算法竞赛进阶指南》
这题应该算换根dp的入门题吧
首先我们进行第一次扫描 可以任选一个根x 算出以x为源点的最大流量是多少
设表示以x为根的子树中,把x作为源点 从x流向子树的最大流量是多少 我们用表示一个点的入度 有如下dp方程
第二次扫描时我们先把根节点的答案计入(因为第一次扫描已经处理好了) 当我们要遍历一个子节点的时候(设为y)
我们要进行换根操作 如何换根呢 我们想 x的流量包括了y和其他x的子节点 那我要以y为根的话 x得是y的子节点 我要把流量改成x到y 那么我要先在x的流量里面减去 y流向x的 再将剩下的流量 由x流向y 这样就可以视作y变成了树根
我们用 表示 将根从x变成y后y流量的结果
然后当y深度遍历完后 在将流量复原(这很类似深搜的时候 回溯的操作 实际也差不多)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
int d[N],h[N],nex[N<<1],to[N<<1],cur;
ll edge[N<<1],D[N],ans;
void add_edge(int x,int y,ll z){
to[++cur]=y;nex[cur]=h[x];edge[cur]=z;h[x]=cur;
}
void dfs(int u,int fath){
for(int i = h[u]; i; i = nex[i]){
int v = to[i];
if(v!=fath){
dfs(v,u);
if(d[v]==1) D[u]+=edge[i];
else D[u]+=min(edge[i],D[v]);
}
}
}
void dfs2(int u,int fath){
ans=max(ans,D[u]);
for(int i = h[u]; i; i = nex[i]){
int v = to[i];
if(v!=fath){
if(d[u]==1) D[v]+=edge[i];
else D[v]+=min(D[u]-min(D[v],edge[i]),edge[i]);
dfs2(v,u);
if(d[u]==1) D[v]-=edge[i];
else D[v]-=min(D[u]-min(D[v],edge[i]),edge[i]);
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
cur=0;
memset(h,0,sizeof(h));
memset(D,0,sizeof(D));
memset(d,0,sizeof(d));
int n;
scanf("%d",&n);
for(int i = 1; i <= n-1; i++){
int u,v;ll w;
scanf("%d%d%lld",&u,&v,&w);
add_edge(u,v,w);add_edge(v,u,w);
d[v]++,d[u]++;
}
dfs(1,0);
ans = 0;
dfs2(1,0);
printf("%lld\n",ans);
}
return 0;
}