[luogu P3360] 偷天换日 解题报告(树形DP)

题目链接:https://www.luogu.org/problemnew/show/P3360

题解:

首先我们把边上的消耗放到向下的点上,如果是叶子节点的话就先做一次0/1背包

发现这是一颗二叉树,转移的时候枚举给左儿子多少时间,右儿子多少时间就好

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;

using std::max;
const int N=1000+15;
ll n,tot=1;
ll dp[N][N],w[N],c[N];
inline ll read()
{
    char ch=getchar();
    ll s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
void dfs(int now)
{
    ll t=read(),x=read();
    t<<=1;
    if (x)
    {
        for (ll i=1;i<=x;i++) w[i]=read(),c[i]=read();
        for (ll i=1;i<=x;i++)
        {
            for (ll j=n;j>=c[i];j--) if (j-c[i]>=t) dp[now][j]=max(dp[now][j],dp[now][j-c[i]]+w[i]);        
        }
        return; 
    }
    ll l=++tot;dfs(tot);
    ll r=++tot;dfs(tot);
    for (ll i=t;i<=n;i++)
        for (ll j=0;j<=i-t;j++)
        {
            dp[now][i]=max(dp[now][i],dp[l][j]+dp[r][i-t-j]);
        }
}
int main()
{
    n=read();n--;
    dfs(1);
    printf("%lld
",dp[1][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/xxzh/p/9672912.html