• POJ3585 Accumulation Degree【换根dp】


    题目传送门

    题意

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

    分析

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

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

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

    经过了$u-v$这条边的答案就是$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
    ",ans); 
    79     }
    80 }
    Code

    完啦。

  • 相关阅读:
    《当程序员的那些狗日日子》(六)继续熬夜学习的日子
    《当程序员的那些狗日日子》(四)喘过气来了
    《当程序员的那些狗日日子》(二)走上不归路
    《当程序员的那些狗日日子》(八)床上等你
    《当程序员的那些狗日日子》(一)毕业后的徘徊
    wince定时拍照功能(转)
    Excel公式找出某一列中是否有某值
    2010年到10月的流水帐
    不错的windows phone的博客
    将同一个表中的一个域更新到另外一个域的SQL文
  • 原文地址:https://www.cnblogs.com/lyttt/p/11804032.html
Copyright © 2020-2023  润新知