代码源#225流(换根dp)

66 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
本文已参与「新人创作礼」活动,一 起开启掘金创作之路。

题目

image.png
题意

就是给我们n个点构成一个树然后我们从根节点灌水每条边都有流量限制问以某一点为根节点时从叶节点出来的水量最大是多少

解法

我们构造一个MinMin数组Min[x]Min[x]表示以xx为父节点的子树可以经过的最大流量是多少

void dfs(int x,int fa) {
    Min[x]  = 0;
    bool f = false;
    for(auto [u,w] : v[x]) {
       if(u == fa) continue;
       f = true;
       dfs(u,x);
       Min[x] += min(w,Min[u]);
    }
    if(!f) Min[x] = 1e18;
}

这样我们进行第一步的dfs我们从下往上就可以记录子树的最大流量如果这个节点是以1为根的树的叶节点那么我们给这个节点的最大流量设为最大(因为如果该节点的子树是不存在的)这样我们就可以通过一遍dfs求出每个节点的MinMin

然后我们讨论换根的影响:

我们令ansans数组表示将XX作为根节点叶节点的流量该变量因为他本身的子树是不受影响的所以由父节点带来的改变是ans[x]+Min[x]min(w,Min[u])ans[x] + Min[x] - min(w,Min[u])但是如果这个节点之前是叶节点那么ans[x]=Min[fa]ans[x] = Min[fa]如果X为结点1时我们需要特判一下

void dfs1(int x,int fa) {
    for(auto [u,w] : v[x]) {
       if(u == fa) continue;
       if(ans[x] + Min[x] - min(w,Min[u])) {
          ans[u] = min(ans[x] + Min[x] - min(w,Min[u]),w);
       }else ans[u] = min(w,Min[fa]);
       dfs1(u,x);
    }
}

输出的时候我们也需要注意一下叶节点时没有子树的

void solve()
{
  int n; cin >> n;
  Min[0] = 1e18;
  rep(i,n - 1) {
     int x,y,w; cin >> x >> y >> w;
     v[x].pb({y,w});
     v[y].pb({x,w});
  }
  dfs(1,0);
  dfs1(1,0);
  // rep(i,n) cout << Min[i] << " \n"[ i == n];
  rep(i,n) {
    if(Min[i] == 1e18) Min[i] = 0;
     cout << Min[i] + ans[i] << endl;
  }
}