后序遍历优化树形背包
大致精神是, 我们先对原来的树进行后序遍历重编号, 然后定义状态 \(f(i,k)\) 为选取前 \(i\) 个物品, 背包容量为 \(k\) 的最大价值.
后序遍历的好处是, 每次新加的点都是子树的根, 并且子树内的顺序在值域上连续.
于是讨论是否选择当前的点 \(i\), 状态转移方程即为:
\[f(i,k)=\max(f(i-1,k-w_i)+v_i,f(i-\text{size}_i,w_i))
\]
时间复杂度 \(O(nW)\).
code:
void dfs(int u,int f)
{
siz[u]=1;
for(int i=h[u];i;i=e[i].nxt)
{
int p=e[i].to;
if(p==f)continue;
dfs(p,u);
siz[u]+=siz[p];
}
dfn[++dfncnt]=u;
}
//-----------------------------------------------------------------
for(int i=1;i<=rtcnt;i++)dfs(rt[i],0);
for(int i=1;i<=n;i++)
for(int j=0;j<=W;j++)
{
int u=dfn[i];
dp[i][j]=dp[i-siz[u]][j];
if(j>=w[u])dp[i][j]=max(dp[i][j],dp[i-1][j-w[u]]+v[u]);
}