JZOJ 5344. 【NOIP2017模拟9.3A组】摘果子

来源:互联网 发布:数据存储管理软件 编辑:程序博客网 时间:2024/06/05 12:31

题目大意

给n个物品,每个物品有他唯一的父亲,选物品的时候选了他的父亲才能选他。
每个物品有代价,有获利。
问在总代价不超过m的情况下能获得的最大获利。

题解

裸的树形背包。
用多叉树转二叉树的方法过不了N2000的数据,超时(时间复杂度O(n3))。
那么我们基于整棵树的DFS序做一遍DP。
f[i][j]表示做到dfs序中的第i个,总代价为j。设dfs序中的第i个点为p号点,那么

f[i][j]=max(f[i+1][jw[p]]+c[p],f[i+siz[p]][j])

其中,f[i+siz[p]][j]表示不选p,那么其子树也不会被选到,f[i+1][jw[p]]+c[p]表示选p。
注意了,这个DP必须倒着做。因为只有这样才能够保证更新到f[i][j]的状态在更新f[i][j]前已经是最优的。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define N 2010#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)#define eg(i,x) for(i=head[x];i;i=edge[i].next)using namespace std;struct note{    int to,next;};note edge[N*2];int i,j,k,l,n,m,x;int u,v,tot,ans;int head[N],c[N],w[N];int dfn[N],tar[N];int f[N][N],fa[N],siz[N];void lb(int x,int y){edge[++tot].to=y;edge[tot].next=head[x];head[x]=tot;}void dfs(int x){    int i;    dfn[x]=++k;    tar[k]=x;siz[x]=1;    eg(i,x)        if(fa[x]!=edge[i].to){            fa[edge[i].to]=x;            dfs(edge[i].to);            siz[x]+=siz[edge[i].to];        }}int main(){    scanf("%d%d",&n,&m);    fo(i,1,n)scanf("%d%d",&c[i],&w[i]);    fo(i,1,n-1){        scanf("%d%d",&u,&v);        lb(u,v);lb(v,u);    }    dfs(1);    memset(f,128,sizeof(f));    f[n+1][0]=0;    fd(i,n,1){        x=tar[i];        fo(j,0,m){            f[i][j]=f[i+siz[x]][j];            if (j>=w[x]) f[i][j]=max(f[i][j],f[i+1][j-w[x]]+c[x]);            if (f[i][j]<0) f[i][j]=0;        }    }    printf("%d",f[1][m]);    return 0;}
原创粉丝点击