背包问题(0/1背包,完全背包,多重背包)

你的背包让我走得好缓慢,借了终于陪着我腐烂

1. 0/1背包

有一个腐烂的背包,容量为v,现有n件物品,每件物品有两个属性,体积v_i,和价格p_i,我们要选择物品往背包里放,使得在物品总容积不超过v的情况下价值最大。

思路:

设 dp[n][v] 前n件物品,背包总容量为v时的最大价值。

1)当我们想要放物品 i 时, 就要为他预留 v_i 的空间,那么背包的总容量就会下降 v_i, 剩下的容量 v - v_i去放其他物品,如果不放物品 i, 那么不用预留空间,空间仍然为 v,对于放还是不放,当然是取决于总价值的大小,即 max( dp[i-1][v-v_i] + p_i, dp[i-1][v]), 这样就从根部引出了两个分支,每个分支又会引出来两个分支,最终形成一棵二叉树,从根到叶子结点,左子树为0,右子树为1,相当于将所有情况枚举了出来,但是区别之处在于不同分支是会出现相同子状态的,这就避免了重复计算。

2)In fact, 某些分支是不一定能到达叶子结点的,即如果某一分支的某个结点加入后,总容量超了,那么这条路径就无法继续延续了。

解法:

1)我们可以用递归函数来求,注意的是要用一个temp[n][v]数组存储已经求过的maxValue(n,v)来避免重复计算,因为到达状态[n,v]的可能不止一种情况。

2)用动态规划,递归是自顶向下,到达边界在回溯,而动态规划则是直接自底向上。

每个maxValue(n,v)关联的状态只有maxValue(n-1,v)或maxValue(n-1,v-Vn).所以我们我们填一个n+1行v+1列的表,从maxValue(0,0)一直到maxValue(n,v),一直迭代。详细解释见【动态规划】01背包问题(通俗易懂,超基础讲解)_Yngz_Miao的博客-CSDN博客_动态规划解决01背包问题

2. 完全背包

在完全背包中是不限制每件物品的数量的,而0/1背包是每种物品只有一件。此时的问题不是针对每种物品放或不放(放0个或1个),而是放几个的问题(放0个,1个,2个。。。。or n个),这里物品i最多放maxSpace//vi件。因此我们在放每件物品的时候要多加一个循环,分别算出maxValue(i,v)在物品i取0至maxSpace//vi件时的值,取最大的作为maxValue[i][v]的最终值。动态规划填表一行一行的填。详细见【动态规划】完全背包问题 - 弗兰克的猫 - 博客园

3.多重背包

每种物品的数量为num[i]个,完全背包的物品数是无限的,多重背包是有限个。和完全背包基本相同,核心是每种物品放几个,从0~num[i], 个数不超过num[i]的同时空间也不能超过背包容量,详细见【动态规划】多重背包问题 - 弗兰克的猫 - 博客园

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


int main(){
    int space,kind;
    cin>>space>>kind;
    vector<vector<int>>data(kind+1,vector<int>(3,0));
    vector<vector<int>>dp(kind+1,vector<int>(space+1,0));

    //space  price  num
    //注意这里是从1开始,只有物品1~kind,没有物品0
    for(int i=1;i<kind+1;i++)cin>>data[i][0]>>data[i][1]>>data[i][2];

    for(int i = 1; i<kind+1;i++){
        for(int j=1;j<space+1;j++){
            for(int k=0;k<=data[i][2]&&j>=k*data[i][0];k++){//0表示不选,注意选的第i件物品总体积不超过当前的容量
                //由于这里考虑的是选k件物品的数量,所有下面每次减掉的空间是k*data[i][0],增加的价值是k*data[i][1]
                    dp[i][j]=max(dp[i][j],dp[i-1][j-k*data[i][0]]+k*data[i][1]);
            }
        }
        

    return 0;
}











我不生产知识,我只是知识的搬运工------农夫

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值