0
点赞
收藏
分享

微信扫一扫

【背包九讲】有依赖的背包问题【树形DP+分组背包优化】

624c95384278 2022-04-04 阅读 48

Date:2022.03.29
题意描述:
有 N 个物品和一个容量是 V 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
在这里插入图片描述
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 1…N。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。
接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数 vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
数据范围
1≤N,V≤100
1≤vi,wi≤100
父节点编号范围:
内部结点:1≤pi≤N;
根节点 pi=−1;
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例:
11

思路:题目有很强的限制即“选择它就必须选择它的父节点”,考虑树形dp, f [ u ] [ j ] : f[u][j]: f[u][j]: u u u为根的子树中,总体积不超过 j j j的最大价值。
如果暴力来看每个点的每个子结点,每个子节点都可选或不选,也就是 2 100 2^{100} 2100,这不t飞了?考虑优化。
观察到每个子结点都可以视作分组背包问题的一组,而对于这些组,背包的总体积是其父节点的第二属性。因此,我们考虑每个子节点为一个分组,在其中至多选择一个属性------给这个组分多少体积!因此对所有分组进行分组背包的遍历后,在每组选一个体积的最大总收益再加上子树的根节点 u u u就是 f [ u ] [ j ] f[u][j] f[u][j],之后就是树形dp。
需要注意,每次算完一个结点别忘了标记所有 f [ u ] [ k ] 【 其 中 k < v [ u ] 】 f[u][k]【其中k<v[u]】 f[u][k]k<v[u]为不合法状态,因为装不下 u u u当然也装不下其子树。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
typedef long long LL;
LL n,m,f[N][N],t,k,v[N],w[N],root;
LL h[N],ne[N],e[N],idx;
void add(LL x,LL y)
{
    e[idx]=y;ne[idx]=h[x];h[x]=idx++;
}
void dfs_down(LL u,LL fa)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        LL t=e[i];
        dfs_down(t,u);
        for(int j=m-v[u];j>=0;j--)
            for(int k=0;k<=j;k++)
                f[u][j]=max(f[u][j],f[u][j-k]+f[t][k]);
    }
    for(int j=m;j>=v[u];j--) f[u][j]=f[u][j-v[u]]+w[u];
    for(int j=0;j<v[u];j++) f[u][j]=0;
}
int main()
{
    cin>>n>>m;memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
    {
        LL idx;cin>>v[i]>>w[i]>>idx;
        if(idx!=-1) add(idx,i);
        else root=i;
    }
    dfs_down(root,-1);
    cout<<f[root][m];
    return 0;
}
举报

相关推荐

0 条评论