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 背包,通过特殊转移顺序保证算法不假。

浙公网安备 33010602011771号