关于01背包的一维数组方法

本文介绍01背包问题的动态规划解决方案,包括二维数组和优化后的一维数组实现方式。详细解析了如何通过动态规划求解背包的最大价值,并提供了具体代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

01背包二维数组动态规划

* 01背包问题
* 给你一个可装载重量为W的背包和N个物品,每个物品有重量和价值两个属性。
* 其中第i个物品的重量为wt[i],价值为val[i],
* 现在让你用这个背包装物品,最多能装的价值是多少?

这是二维数组的代码,N个物品为外循环,W容量为内循环

int packaging(int N,int W,int[] wt,int[] val){
        int [][] dp = new int[N+1][W+1];
        for (int i = 1;i<=N;i++){
            for(int j = 1;j<=W;j++){
                if(j<wt[i-1]){
                    dp[i][j] = dp[i-1][j];//容量不够了,只能不放当前物品
                }else{
                    dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-wt[i-1]]+val[i-1]);//wt[i-1]和val[i-1]就是第i个数
                    //不放和放比较,取大的;
                    //放:N-1个物品,W减去当前物品重量的容量---此情况下的最大值+放了当前物品的值=dp[i][j]
                }
            }
        }
        return dp[N][W];
    }

在这里插入图片描述

一维数组方法

其实二维数组的填充顺序就是一层一层的,其中每层都是从左往右。且每个dp[i][j]都是由上一层的某几个元素得来的,如下所示,红圈dp[4][8]由黄圈得来,即:如果第四个物品能够放入,即当前容积大于物品4的大小,dp[4][8]就是放入和不放入取最大值:4+6和11。
所以可以缩减dp[][]为一维,每一层重复使用,覆盖上一层的数即可。

在这里插入图片描述
所以在dp[]内部,其每一位的数字都是一个一个的被这一层覆盖的,未覆盖前,其值还是上一层的在j位的值。如下图所示,在计算dp[4][8]时,dp[]中的数值其实是黄圈中的数值。我们需要的值已经被当前层的覆盖了。
在这里插入图片描述
所以每一层覆盖时,应该从后往前。即内循环改为从后往前。如下图
在这里插入图片描述
此时计算dp[4][8]所需的dp[3][3]和dp[3][8]都未被覆盖,即dp[8] = max(dp[8],dp[3]);代码如下

int packaging_oneDim (int N,int W,int[] wt,int[] val){
        int dp[] = new int[1+W];
        for (int i = 1; i <= N; i++) {
            for (int j = W; j >=1; j--) {
                if(j<wt[i-1]){
                    dp[j] = dp[j];
                }else {
                    dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
                }
            }
            System.out.println(Arrays.toString(dp));
        }
        return dp[W];
    }
  • 在j<wt[i-1]时,原地赋值,所以内循环可优化
for (int j = W; j >=1; j--) {
                if(j>=wt[i-1]){
                    dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
                }
            }

即:j<当前物品大小的就不用看了,肯定不能选,所以进一步优化

for (int j = W; j >=wt[i-1]; j--) {
         dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1])}

要求恰好装满

在这里插入图片描述

int packaging_oneDim_exactly (int N,int W,int[] wt,int[] val){
        int dp[] = new int[1+W];
        for (int i = 1; i < dp.length; i++) {
            dp[i] = Integer.MIN_VALUE;
        }
        for (int i = 1; i <= N; i++) {
            for (int j = W; j >=1; j--) {
                if(j<wt[i-1]){
                    dp[j] = dp[j];
                }else {//大于表示当前可以放,需要后续再放
                    dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
                }
            }
        }
        return dp[W]<0?-1:dp[W];
    }

思路以及截图来自小码哥教育。

<think>嗯,我现在需要了解C++中的01背包问题的一维数组解法。我之前学过动态规划,但可能对优化空间复杂度不太熟悉。首先,我得回忆一下01背包问题的基本概念。 01背包问题应该是给定一组物品,每个物品有重量和价值,在背包容量限制下选择物品使得总价值最大,且每个物品只能选一次。传统的二维动态规划方法是用dp[i][j]表示前i个物品在容量j时的最大价值。状态转移方程大概是dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i])。这样需要O(n*capacity)的空间,当容量很大时可能会占用较多内存。 现在问题是优化到一维数组。用户想知道如何用一维数组实现。那怎么压缩空间呢?我记得可能需要改变遍历的顺序。比如,二维情况下,每个状态依赖于上一行的左边状态,所以如果一维的话,可能需要逆序遍历容量,这样在更新的时候不会覆盖掉之前需要用的数据。 比如,假设用一维数组dp[j],其中dp[j]表示容量为j时的最大价值。初始化时,所有dp[j]都是0。然后对于每个物品,从后往前遍历容量,从capacity到weight[i],这样在更新dp[j]的时候,用到的dp[j - weight[i]]还是上一轮的结果,也就是没有选当前物品的状态。这样就能保证每个物品只被选一次。 举个例子,比如当前处理第i个物品,重量是w,价值是v。在二维数组中,处理第i行的时候,每个dp[i][j]依赖于dp[i-1][j]和dp[i-1][j - w] + v。而一维的情况下,如果正序遍历,那么前面的j - w可能已经被更新过了,导致重复选物品,这不符合01背包的要求。所以必须逆序处理容量。 那具体的代码结构应该是怎样的呢?大概的步骤是: 1. 初始化一个一维数组dp,大小为capacity+1,初始值为0。 2. 遍历每个物品,对于每个物品i,遍历j从capacity到weight[i](包括weight[i]),因为如果j < weight[i],当前物品放不下,不需要更新。 3. 对于每个j,dp[j] = max(dp[j], dp[j - weight[i]] + value[i])。 4. 最终dp[capacity]就是最大价值。 那这个一维数组的时间复杂度应该还是O(n*capacity),但空间复杂度降到了O(capacity),这确实节省了很多空间,特别是当capacity很大时,比如上百万的时候,二维数组可能无法处理,而一维数组可能可以。 需要注意的问题可能有:遍历的顺序必须是逆序的,否则会导致完全背包问题的效果(即物品可以被选多次)。还有,初始化的时候dp[0]是0,其他可能也是0,或者根据问题要求是否恰好装满的情况可能有所不同。比如,如果要求恰好装满,可能需要将dp初始化为负无穷,只有dp[0]为0,这样只有在恰好装满的情况下才有解。 比如,当题目是恰好装满背包的最大价值时,初始化可能不同。但通常情况下,01背包问题可能允许不超过容量,这时候初始化为0即可。 举个例子,假设物品有重量数组weights和值数组values,容量为cap。那么代码大概是这样的: vector<int> dp(cap + 1, 0); for(int i = 0; i < n; ++i){ for(int j = cap; j >= weights[i]; --j){ dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[cap]; 这样就能得到最大价值。那这应该就是正确的一维数组实现方式。 总结来说,01背包一维数组解法的关键点在于逆序遍历容量,确保每个物品只被考虑一次。这样可以节省空间,同时保持正确性。</think>在C++中,**01背包问题的一维数组解法**通过优化动态规划的空间复杂度,将原本的二维数组压缩为一维数组,从而更高效地解决问题。以下是核心要点和实现步骤: --- ### **核心思想** - **状态定义**:`dp[j]` 表示容量为 `j` 的背包能装下的最大价值。 - **逆序遍历**:为避免重复选取物品(保证每个物品仅选一次),容量需从大到小遍历。 - **状态转移**:`dp[j] = max(dp[j], dp[j - weight[i]] + value[i])`,决策是否选择当前物品。 --- ### **实现步骤** 1. **初始化数组** 创建一维数组 `dp`,大小为 `capacity + 1`,初始值全为0(若要求恰好装满背包,需初始化为负无穷,仅 `dp[0] = 0`)。 2. **遍历每个物品** 对每个物品的重量 `weight[i]` 和价值 `value[i]`,从背包容量最大值逆向更新到当前物品重量。 3. **更新状态** 通过比较“不选当前物品”和“选当前物品”的价值,更新 `dp[j]`。 ```cpp #include <vector> using namespace std; int knapsack01_1D(vector<int>& weights, vector<int>& values, int capacity) { vector<int> dp(capacity + 1, 0); int n = weights.size(); for (int i = 0; i < n; ++i) { for (int j = capacity; j >= weights[i]; --j) { dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[capacity]; } ``` --- ### **关键点** - **逆序原因**:正序遍历会导致小容量状态被覆盖,从而允许重复选择物品(变为完全背包问题)。 - **空间优化**:时间复杂度仍为 O(n·capacity),但空间复杂度从 O(n·capacity) 降至 O(capacity)。 - **应用场景**:适用于物品数量多或背包容量较大的情况,如竞赛题目或实际工程中的资源分配问题。 --- ### **示例说明** 假设物品信息如下: - 重量:`weights = {2, 3, 4, 5}` - 价值:`values = {3, 4, 5, 6}` - 背包容量:`capacity = 8` 最终 `dp[8]` 的值为最大价值(此例为10,选择重量3和5的物品)。 通过一维数组解法,既能高效计算最大价值,又显著节省内存空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值