题目链接: 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;
}