把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

POJ3585 Accumulation Degree【换根dp】

题目传送门

题意

给出一棵树,树上的边都有容量,在树上任意选一个点作为根,使得往外流(到叶节点,叶节点可以接受无限多的流量)的流量最大。

分析

首先,还是从1号点工具人开始dfs,可以求出dp[i]为点i向它的子树中可以获得的最大流量。

接下来考虑换根,设f[i]是以i为根节点的答案(向它的所有根节点能够发射的最大流量之和)

 

考虑把根从u换到vv自己子树内的答案dp[v]肯定是在f[v]之内的

经过了uv这条边的答案就是min(f[u]min(w,dp[v]),w)

加起来就是f[v]=dp[v]+min(f[u]min(w,dp[v]),w)

理解一下:f[u]min(w,dp[v])是减去红圈里的贡献,也就是u不往v里面流也产生的答案。要取min(w,dp[v])是因为u真正能流进v子树里的流量还要受到w的限制

相同地,把v当做根之后往u方向流的流量也会受到w的限制,所以也要取min

 

另外,特别地,还有这种情况:

 

 

(这种情况真的好难想到的说)

这种情况的话,答案就直接是f[v]=dp[v]+w

然而用上面的式子的话,f[u]min(w,dp[v])=0,变成f[v]=dp[v],是不成立的。所以需要特判一下。

然后就做

复制代码
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<vector>
 4 #include<cstring>
 5 using namespace std;
 6 #define N 200005
 7 #define ll long long
 8 #define INF 0x3f3f3f3f
 9 int n,ans;
10 int dp[N],f[N];
11 vector<pair<int,int> >G[N];
12 int rd()
13 {
14     int f=1,x=0;char c=getchar();
15     while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
16     while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
17     return f*x;
18 }
19 void dfs(int u,int p)
20 {
21     int tmp=0;
22     for(int i=0;i<G[u].size();i++)
23     {
24         int v=G[u][i].first,w=G[u][i].second;
25         if(v==p) continue;
26         dfs(v,u);
27         tmp+=min(dp[v],w);
28     }
29     if(tmp) dp[u]=tmp;
30     return ;
31 }
32 void dfs2(int u,int p)
33 {
34     ans=max(ans,f[u]);
35     for(int i=0;i<G[u].size();i++)
36     {
37         int v=G[u][i].first,w=G[u][i].second;
38         if(v==p) continue;
39         if(G[u].size()==1)
40         {
41             f[v]=dp[v]+w;
42             dfs2(v,u);
43         }
44         else
45         {
46             f[v]=dp[v]+min(w,f[u]-min(w,dp[v]));
47             dfs2(v,u);
48         }
49     }
50 }
51 int main() 
52 {
53     int T=rd();
54     while(T--)
55     {
56         n=rd();
57         if(n==0)
58         {//特判 
59             puts("0");
60             continue;
61         }
62         ans=0;
63         memset(dp,INF,sizeof(dp));
64         for(int i=1;i<=n;i++)
65             G[i].clear();
66         for(int i=1;i<n;i++)
67         {
68             int u=rd(),v=rd(),w=rd();
69             G[u].push_back(make_pair(v,w));
70             G[v].push_back(make_pair(u,w));
71         }
72         dfs(1,-1);
73         for(int i=1;i<=n;i++)
74             if(dp[i]==INF)
75                 dp[i]=0;//叶节点
76         f[1]=dp[1];
77         dfs2(1,-1);
78         printf("%d\n",ans); 
79     }
80 }
Code
复制代码

 

完啦。

 

 

 

posted @   Starlight_Glimmer  阅读(145)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示