P1270 【“访问”美术馆】

$large{ ext{一千个Oier程序中有一千种树形DP}}$

思路都差不多的,但是每个人都有自己的状态定义与转移

不妨定义$dp[i][j]$表示,在$i$子树内,偷$j$张画,且不考虑根到$i$父节点路径代价的最短时间

$a[i]$表示$i$与其父节点的距离

$d[i]$表示$i$到根节点的距离

不难想出转移

$large{dp[i][j]=minleft{dp[son1][k]+dp[son2][j-k]+dis[x] ight}}$

统计答案

枚举每个节点选几个,如果$dp[i][j]+d[i]-a[i]<=s$,更新答案

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int size[1010],s,cnt,a[1010],v[1010],e[1010][2],dp[1010][2010],ans,d[1010];
void init(int x)
{
    int t,to;
    if(scanf("%d%d",&t,&to)!=2)
        return;
    d[x]+=2*t,a[x]=2*t,dp[x][0]=0;
    if(to)
        v[x]=to;
    else
    {
        d[++cnt]=d[x],init(e[x][0]=cnt);
        d[++cnt]=d[x],init(e[x][1]=cnt);
    }
}
void dfs(int x)
{
    if(!x)
        return;
    dfs(e[x][0]),dfs(e[x][1]);
    size[x]=v[x]+size[e[x][0]]+size[e[x][1]];
    if(v[x])
        for(int i=1;i<=v[x];i++)
            dp[x][i]=a[x]+dp[x][0]+5*i;
    else
        for(int i=1;i<=size[x];i++)
            for(int j=0;j<=i;j++)
                dp[x][i]=min(dp[x][i],dp[e[x][0]][j]+dp[e[x][1]][i-j]+a[x]);
}
int main()
{
    scanf("%d",&s);
    memset(dp,0x3f,sizeof(dp));
    init(++cnt);
    dfs(1);
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=size[i];j++)
            if(dp[i][j]+d[i]-a[i]<s)
                ans=max(ans,j);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/ivanovcraft/p/9776928.html