P6223 PODJELA

2023.10.6 16:46 luogu solution

一道不错的树上背包题。

为了方便,我们先让拿到的钱减去给定值。那么此时因为要使所有农民的值 0 而每个节点只能通过它的祖先和其他的而非其子树节点沟通,所以我们先对于每个子树,让其所有非根值 0 求最小次数。这样参数还不够,那我们便在对应限制次数的基础上贪心地让根的值最大。设 dpi,u 表示操作 i 次使得 u 的子树全达标,u 的最大值,那么对于此时我们加入的子节点 v,假设它的限制此数为 j,那么:
dpv,j<0 时,我们一定要花一次使得其大于 0,那么有:

gi+j+1=max{gi+j+1,dpv,j+dpu,i}

g[] 是临时数组)
否则,我们可以选择是否与 v 对应子树合并,有:

gi+j+1=max{gi+j+1,dpv,j+dpu,i}gi+j=max{gi+j,dpu,i}

然后注意一下代码细节即可。(用临时数组是因为我经常搞错转移顺序,而且有些背包题不能直接转移必须要临时数组,所以这里就直接用了)

Code

int n,f[N][N],siz[N],g[N];
vector<int>F[N];
void dfs(int x,int fa){
for(int y:F[x])if(y^fa){
dfs(y,x);
for(int i=0;i<=siz[x]+siz[y];i++)g[i]=-1e9;//赋初值
for(int i=0;i<siz[x];i++)
for(int j=0;j<siz[y];j++){
if(f[j][y]>=0)//可以不合并
g[i+j]=max(g[i+j],f[i][x]);
g[i+j+1]=max(g[i+j+1],f[i][x]+f[j][y]);//合并
}
for(int i=(siz[x]+=siz[y]);~i;--i)//赋值回去
f[i][x]=g[i];
}
}
int main(){
n=read();
memset(f,-0x3f,sizeof(f));//赋初值
for(int i=1,V=read();i<=n;i++)
f[0][i]=V-read(),siz[i]=1;
for(int i=1,u,v;i<n;i++)
F[u=read()].push_back(v=read()),F[v].push_back(u);
dfs(1,0);
for(int i=0;i<=n;i++)
if(f[i][1]>=0)
return printf("%d",i),0;
}

end:17:01

本文作者:NBestの思潮

本文链接:https://www.cnblogs.com/NBest/p/17744730.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   NBest  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起