「动态规划」0/1背包问题

问题描述

给定n种物品和一个背包,物品i的重量是wi,其价值为 v i,背包的容量为C。
背包问题是如何选择装入背包的物品,使得装入背包中物品的总价值最大? 如果在选择 装入背包的物品时,对每种物品i只有两种选择:装入背包或不装入背包,即不能将物品i装入背包多次,也不能只装入物品i的一部分,则称为0/1背包问题
在这里插入图片描述

思路分析

1. 确定状态转移

在0/1背包问题中首先需要考虑的限制条件就是:如果当前的第 i 个物品的重量大于了背包的剩余容量,那么就不能装入。

如果能装入,那我们再考虑到底要不要装入?也就是从 “装入” 和 “不装入”两个方面来获得背包的最大价值。

因此定义dp数组:dp [ i ] [ j ]
表示当背包的容量为 j 的时候,装入前 i 个物品可以获得的最大价值。
上述对 i 的解释是错误的,如果能装入前 i 个,那么总价值已经确定了,就没必要算了。

所以正确的解释是:
表示当背包的容量为 j 的时候,对于前 i 个物品,可以获得的最大价值。也就是可装可不装。

2. 求出装入背包的具体物品

即获得我们本次算出的最优解,不仅是得出结果。

我们用一个一维数组 isLoad[ i ] 来表示物品 i 有没有被装入。值为1表示装入,为0表示未装入。

为了确定装入背包的具体物品,我们从 dp[ n ] [ w ] 往前推。如果dp[ n ] [ w ] > dp [ n - 1] [ w ],则说明第n个物品肯定被装入了背包,因为此时背包的价值更大。否则物品n肯定没有被放入背包。一个if-else就可以解决问题。

注意点

在下面代码中,weight和value数组的下标是从1开始的,对应第 i 个物品。0下标没有用,数值用0代替。

代码

#include <iostream>
#include <math.h>
using namespace std;

int knapSack(int n, int w, int weight[], int value[], int isLoad[]) {
    // 多开辟的一个空间是为了容量为0和不装东西的情况
    int dp[n+1][w+1]; // 定义dp数组 表示当容量为w的时候,对于前i个物品可以获得的最大价值 
    for(int i = 0 ; i <= n; i++) dp[i][0] = 0; // 容量为0
    for(int i = 0 ; i <= w ; i++) dp[0][i] = 0; // 不装东西

    // 动态规划
    for(int i = 1 ; i <= n; i++) {
        for(int j = 1 ; j <= w ; j++) {
            // 准备放第i个物品
            if(j < weight[i]) { // 背包的剩余容量不够了 就不放
                dp[i][j] = dp[i-1][j]; // 不装
            } else {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
            }
        }
    }

    // 获得装入背包的物品 存放在isLoad数组中
    int j = w;
    for(int i = n ; i >= 1 ; i--) { // 第i个物品
        if(dp[i][j] > dp[i-1][j]) {
            isLoad[i] = 1;
            j -= weight[i];
        } 
        else {
            isLoad[i] = 0;
        }
    }

    return dp[n][w];
}

int main() {
    int n = 3;
    int w = 4;
    int weight[4] = {0,2,1,3};
    int value[4] = {0,4,2,3}; // 下标从1开始 0下标不用 直接设置为0
    int isLoad[4];
    int maxValue = knapSack(n,w,weight,value,isLoad);
    cout<<"最大价值是"<<maxValue;
    for(int i = 1; i <=n ; i++) cout<<isLoad[i]<< " ";
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值