LibreP160 树形背包(dfn-dp 设计)

这是完全版本树形背包。
打一个玄学上下界 \(O(nW^2)\) 的暴力不难。

#include<bits/stdc++.h>
using namespace std;
const int N=50009;
int w[N],v[N],siz[N],W,n;
vector<int> o[N],dp[N];
void dfs(int u){
    if(w[u]>W) return;
    siz[u]=w[u],dp[u][w[u]]=v[u];
    for(int i:o[u]){
        dfs(i); if(w[i]+w[u]>W) continue;
        for(int a=min(W-w[i],siz[u]);a>=w[u];a--)
            for(int b=w[i];b<=min(W-a,siz[i]);b++)
                dp[u][a+b]=max(dp[u][a+b],dp[u][a]+dp[i][b]);
        siz[u]+=siz[i],siz[u]=min(siz[u],W);
    }
}
int main(){
    scanf("%d%d",&n,&W);
    for(int i=0;i<=n;i++) dp[i].resize(W+10);
    for(int i=1,x;i<=n;i++) scanf("%d",&x),o[x].push_back(i);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    dfs(0);
    int ans=INT_MIN; for(int i=0;i<=W;i++) ans=max(ans,dp[0][i]);
    printf("%d",ans);
    return 0;
}

至于 dfn-dp 设计,请看 Link

#include<bits/stdc++.h>
using namespace std;
const int N=50009;
int w[N],v[N],siz[N],rnk[N],dfnt,W,n;
vector<int> o[N],dp[N];
void dfs(int u){
    if(w[u]>W) return;
    siz[u]=1;
    for(int i:o[u])
        dfs(i),siz[u]+=(w[i]+w[u]<=W)?siz[i]:0;
    rnk[++dfnt]=u;
}
int main(){
    scanf("%d%d",&n,&W);
    for(int i=0;i<=n+5;i++) dp[i].resize(W+10);
    for(int i=1,x;i<=n;i++) scanf("%d",&x),o[x].push_back(i);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    dfs(0);
    for(int i=1;i<=n+1;i++) for(int j=0;j<=W;j++){
        dp[i][j]=dp[i-siz[rnk[i]]][j];
        if(j>=w[rnk[i]]) dp[i][j]=max(dp[i][j],dp[i-1][j-w[rnk[i]]]+v[rnk[i]]);
    }
    printf("%d\n",dp[n+1][W]);
    return 0;
}

后记:
前几天想了一下,上面那个 dfs 属性暴力背包没什么无效状态了,为什么不优呢?
原来,下面那个是把分组背包转化成 01 背包,通过特殊转移顺序保证算法不假。

posted @ 2026-02-20 21:55  2025ing  阅读(0)  评论(0)    收藏  举报