前言
眾所周知,背包是可以掛在樹上的。
有依賴的背包問題
顧名思義,有依賴的背包里的物品的選擇是有依賴的廢話即選擇一個物品,就必須先選某個物品。這個必須先選的物品我們稱之為依賴物品。一般地,某個物品的依賴物品只有一個(如果有多個的話可以考慮把出題人掛在樹上)(但某個物品可以同時被多個物品依賴)
首先我們得表示出來物品的依賴關系,考慮到物品i的依賴物品只有1個,所以可以用父子關系來表示,自然而然的想到用樹。
對於物品i,我們要dp出以i為根的子樹中,體積為j時的最大權值和。考慮i的每個兒子,(由於是從下往上dp,所以i的兒子的dp值已經算好了)我們需要考慮從以i的第j個兒子為根的子樹中選幾個節點,同時我們已經知道了第j個兒子的所有dp值,所以不妨把以第j個兒子為根的子樹看做一組物品,且我們已經知道分配給這組物品x的體積時,最大值是多少。
所以就相當於對每個節點做分組背包。同時注意一點,在考慮以i為根的子樹的時候,點i是必選的,所以i會占去1的體積。要注意這一點。
由於窩比較菜,不會直接推dp式子,所以窩采用一個輔助數組,先進行不考慮i的dp。之后再轉化成真實的dp值。
還是康康蒟蒻的代碼叭
void dfs(int u,int fa)
{
sum[u]=1;
for(int e=head[u];e;e=ed[e].nxt)
{
int v=ed[e].to;
if(v==fa)continue;
dfs(v,u);
sum[u]+=sum[v];//sum[i]表示以i為根的子樹的大小
}
int t=0;
for(int e=head[u];e;e=ed[e].nxt)
{
int v=ed[e].to;
if(v==fa)continue;
zz[++t]=v;//是指針不是z z(記錄i節點的第t個兒子的編號)
}
if(!t)
{
val[u][1]=zhi[u];
return ;
}
memset(dp,0,sizeof(dp));//這里dp就是輔助數組,為了不WA,要每次memset一遍
for(int i=1;i<=t;i++)//接下來就是個分組背包
{
for(int j=sum[u]-1;j>=0;j--)
{
for(int r=1;r<=sum[zz[i]];r++)
{
if(j-r>=0)
dp[j]=max(dp[j],dp[j-r]+val[zz[i]][r]);
}
}
}
for(int i=sum[u];i>=1;i--)
dp[i]=dp[i-1]+zhi[u];//更新成真正的dp值
for(int i=1;i<=sum[u];i++)
val[u][i]=dp[i];
}
一點注意:注意這里zz數組一定要在dfs(v,u)之后記錄。