依赖的背包问题(c++)

该博客主要是对于Acwing上题解的一个解释与记录

贴上大佬的题解,感谢da

AcWing 10. 有依赖的背包问题(思路不同于dxc,但是个人感觉更好理解) - AcWing

有 NN 个物品和一个容量是 VV 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

如下图所示:

 

如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。

每件物品的编号是 ii,体积是 vivi,价值是 wiwi,依赖的父节点编号是 pipi。物品的下标范围是 1…N1…N。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,VN,V,用空格隔开,分别表示物品个数和背包容量。

接下来有 NN 行数据,每行数据表示一个物品。
第 ii 行有三个整数 vi,wi,pivi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1pi=−1,表示根节点。 数据保证所有物品构成一棵树。

输出格式

输出一个整数,表示最大价值。

数据范围

1≤N,V≤1001≤N,V≤100
1≤vi,wi≤1001≤vi,wi≤100

父节点编号范围:

  • 内部结点:1≤pi≤N1≤pi≤N;
  • 根节点 pi=−1pi=−1;

输入样例

5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2

输出样例:

11

首先分析一下变量和状态

这次我们的所有状态的集合是以x作为根结点的以j为背包的上限容量的集合。其中存的值的大小代表着最大价值。

翻译一下就是DP[x][j]是以x作为根结点,j为最大容量的放物品的最大价值。所以我们最后算出来的结果就在DP[root][volumn]当中

在每一个状态被处理的时候,我们将其看作分组的背包问题,但是这个分组指的是每个子树,是每个子树的最大容积,换句话说就是DP[x][j]=max(DP[x][j],DP[x][j-k]+DP[y][k])//y是x的子树之一的根结点也就是x的孩子结点之一,将k的容积划分给孩子结点来用的最大价值+j-k的容积给自己(该根结点本根)来用的和之前存下来的该组的划分不同的k容积大小给子树的结果相比较最后取最大的情况作为给以x为根结点的j为最大容积的最大价值。

也就是说k是一个互斥的情况,是分组背包问题中,组内的物品,每组只能选一个或者不选。

将该结点的所有子树都尝试使用过了之后,就返回上一层,给上一层的根结点来使用。

而树形结构的创建也是一直让我很头疼的问题,yxc大佬的邻接表又很难懂,我准备用链表来做但是又感觉这种输入给的是各自的前驱的记录感觉很难搞,最后看到了大佬的解法。

vector<int> g[110];//建立

而在记录自己的父亲结点是谁的时候只要这样的操作就可以把自己悬挂在父结点下面了

for(i=1;i为结点序号,i小于等于总结点数量;i++)
{
    cin>>fa;
    g[fa]=i;
}

这样的话遍历自己的所有孩子也方便起来了。

而有些控制在大佬的代码里面也都有了,直接上大佬的代码

转载出处AcWing 10. 有依赖的背包问题(思路不同于dxc,但是个人感觉更好理解) - AcWing

#include<iostream>
#include<vector>
using namespace std;
int f[110][110];//f[x][v]表达选择以x为子树的物品,在容量不超过v时所获得的最大价值
vector<int> g[110];
int v[110],w[110];
int n,m,root;

int dfs(int x)
{
    for(int i=v[x];i<=m;i++) f[x][i]=w[x];//点x必须选,所以初始化f[x][v[x] ~ m]= w[x]
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        dfs(y);
        for(int j=m;j>=v[x];j--)//j的范围为v[x]~m, 小于v[x]无法选择以x为子树的物品
        {
            for(int k=0;k<=j-v[x];k++)//分给子树y的空间不能大于j-v[x],不然都无法选根物品x
            {
                f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
            }
        }
    }
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int fa;
        cin>>v[i]>>w[i]>>fa;
        if(fa==-1)
            root=i;
        else
            g[fa].push_back(i);
    }
    dfs(root);
    cout<<f[root][m];
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值