动态规划背包问题优化空间复杂度——滚动数组

动态规划背包问题优化空间复杂度——滚动数组

链接:代码随想录背包问题

背包问题

在这里插入图片描述

  背包问题是动态规划中基本的问题,我们考虑下面的简单问题:
在这里插入图片描述
  假设背包容量为4,有物品0,1,2,要使背包放的物品价值最大,并且总重量不超过背包容量,应该怎么放?
二维数组下的做法如下:
1、创建二维数组dp,其中dp[i][j]的含义是在0~i物品中任意取,放到容量为j的背包里,能够获得的最大价值。
2、确定递推公式
对于dp[i][j],假设不取第i个物品,那么dp[i][j]=dp[i-1][j];假设取第i的物品,那么dp[i][j]=dp[i-1][j-weight[i]]+value[i]。为了获得更大的物品价值,dp[i][j]要取两者的最大值,即递推公式为:

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

3、初始化dp数组
背包容量为0时,对于j=0时,dp[i][0]=0;对于i=0时,只有当j>=weight[i]时,dp[0][j]=0。在这个例子中初始化dp如下:
在这里插入图片描述
4、确定遍历顺序
可以先遍历背包,再遍历物品,也可以先遍历物品,再遍历背包。
5、举例推导dp数组
在这里插入图片描述
注意:做动态规划时,最好把dp能写出来,遇到有报错可以打印dp数组进行对比,看哪里出错。

空间复杂度优化

  二维数组可以优化成一维数组,即将一个一维数组重复利用,称之为滚动数组。只保留一个和背包容量+1的一维数组,遍历物品,对每个物品,更新这个一维数组,由上面的递推公式,dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]),可以改写成dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]),相当于更新dp[i][j]前,它表示上一层的dp[i-1][j],更新后,它表示dp[i][j]。而如果我们从小容量背包到大容量背包遍历,会出现前面小容量的背包不再是上一层的dp,而是已经更新过的dp,因此这样遍历会出现重复计算物品的现象。因此对于背包应该从大背包向小背包遍历。五部曲分析:
1、确定dp和含义,dp是长度和背包长度+1的一维数组,dp[j]表示容量为j的背包能装的最大物品价值
2、确定递推公式
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
3、dp数组初始化
由于dp[j]代表容量为1的背包装的最大物品价值,因此j=0时dp[0]=0,其余情况也可以赋dp[j]=0,因为后面会将dp[j]覆盖。
4、遍历顺序
只能从后往前遍历,即从大背包向小背包遍历,防止之前的数据被覆盖。
5、举例推导dp数组
在这里插入图片描述

Java代码

public static void main(String[] args) {
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWight = 4;
    testWeightBagProblem(weight, value, bagWight);
}

public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
    int wLen = weight.length;
    //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
    int[] dp = new int[bagWeight + 1];
    //遍历顺序:先遍历物品,再遍历背包容量
    for (int i = 0; i < wLen; i++){
        for (int j = bagWeight; j >= weight[i]; j--){
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    //打印dp数组
    for (int j = 0; j <= bagWeight; j++){
        System.out.print(dp[j] + " ");
    }
}
### 01 背包问题中的动态规划分析 #### 时间复杂度分析 对于 01 背包问题,其核心在于通过动态规划算法计算最优解。假设背包的最大容量为 \( C \),物品数量为 \( n \)。在标准实现中,外层循环遍历所有物品(共 \( n \) 次),而内层循环则针对每种可能的背包容量进行更新操作(最多 \( C \) 次)。因此,总的时间复杂度可以表示为 \( O(nC) \)[^1]。 这种时间复杂度主要来源于两部分嵌套迭代:一是对每个物品逐一考虑;二是基于当前剩余容量评估是否应加入该物品以最大化收益。值得注意的是,在实际应用过程中,如果输入数据具有某些特殊性质,则可以通过剪枝或其他技术进一步降低平均运行时间[^2]。 #### 空间复杂度分析 原始版本的动态规划方法通常会创建一个二维数组 dp[i][j] 来存储前 i 件商品填充到体积不超过 j 的最大价值。这使得初始的空间需求达到 \( O(nC) \)。然而,观察发现每次仅需访问上一层的状态即可完成当前层状态转移方程的求解过程。于是我们能够采用滚动数组技巧来减少内存消耗至一维形式,从而将整体所需空间压缩成线性的规模——即只需保留大小为 \( C+1 \) 的辅助向量就足够支持整个运算流程了[^3]。 以下是利用 Python 实现的一个简单例子展示如何运用上述原理解决基本型别的零壹背包难题: ```python def knapsack_01(values, weights, capacity): n = len(values) dp = [0]*(capacity + 1) for i in range(1, n + 1): wi = weights[i-1] vi = values[i-1] for j in reversed(range(capacity + 1)): if j >= wi: dp[j] = max(dp[j], dp[j - wi] + vi ) return dp[-1] ``` 此函数接受三个参数列表 `values` 和 `weights`, 还有一个整数变量 `capacity`. 它返回当给定这些条件下的最高可获得的价值.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值