01背包的一维和二维dp解法

问题:
背包最大容量为4,物品如下,问背包能背的物品最大价值为多少?
在这里插入图片描述
背包问题关键就在于:对于每个物品,放还是不放,带着这个思想去理解递推公式就很容易了,下面是代码:

def bag_2Dim():  # 01背包的二维dp解法
    # dp[i][j]表示从下标为[0-i]的物品⾥任意取,放进容量为j的背包,价值总和最⼤是多少
    m = 3  # 物品数量
    n = 4  # 背包最大容量
    weight = [1, 3, 4]
    value = [15, 20, 30]
    # m = int(input())  # ACM输入模式
    # n = int(input())
    # weight = list(map(int, input().split()))
    # value = list(map(int, input().split()))

    dp = [[0] * (n+1) for _ in range(m)]
    # 初始化状态
    for i in range(m):
        dp[i][0] = 0  # 背包容量为0时,最大价值一定为0
    for j in range(n+1):  # 不同背包容量下装第0个物品的最大价值
        if j < weight[0]:
            dp[0][j] = 0
        else:
            dp[0][j] = value[0]
    # print(dp)
    for i in range(1, m):  # 先物品
        for j in range(1, n+1):  # 后正序背包
            dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i])
    return dp[m-1][n]

二维解法比较基础,可以将状态压缩,只留背包容量这一状态。

def bag_1Dim():  # 01背包的一维dp解法
    # dp[j]表示容量为j的背包,所背物品的最大价值价值
    m = 3  # 物品数量
    n = 4  # 背包最大容量
    weight = [1, 3, 4]
    value = [15, 20, 30]

    dp = [0] * (n+1)
    for i in range(m):  # 先物品
        for j in range(n, -1, -1):  # 后倒序背包
            if j >= weight[i]:  # 只有能装下该物品时才能操作
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
    return max(dp)

注意一维dp解法中,遍历背包顺序是从大到小倒序遍历,因为如果从小到大遍历的话,每次取得的状态会和之前取得的状态重合,每个物品被放入了不止一次。如果是完全背包问题(每件物品有无限个),就得从小到大遍历了,也就是每个物品可以放入背包多次。

<think>好的,我现在需要解决用户关于完全背包问题的一维解法的提问。首,我得回忆一下完全背包问题的基本概念。完全背包与0-1背包的不同之处在于,每种物品可以选取无限次。因此,在状态转移方程上会有区别。 用户要求的是使用一维数组的解法,也就是空间优化后的动态规划方法。我需要回顾传统的二维解法,然后思考如何压缩成一维数组。对于完全背包二维的状态转移方程通常是dp[i][j] = max(dp[i-1][j], dp[i-1][j- w[i]]+ v[i])。但这里可能记错了,因为完全背包允许重复选择,所以正确的二维方程应该是dp[i][j]= max(dp[i-1][j], dp[i][j - w[i]] + v[i]),因为当选择当前物品时,可以继续在同一行更新,而不是上一行。接下来考虑一维数组的优化。0-1背包一维解法需要逆序遍历容量,以防止重复选取。而完全背包因为可以重复选,所以应该正序遍历容量。这样,当计算较大的容量时,之前已经更新过的小容量可能已经包含了当前物品的选取,从而实现了多次选取的效果。 现在需要确定状态转移方程。用一维数组dp[j]表示容量为j时的最大价值。对于每个物品i,遍历j从w[i]到背包容量C,更新dp[j]为max(dp[j], dp[j -w[i]] +v[i])。这是因为在正序的情况下,每次更新j时,j- w[i]的位置可能已经被当前物品处理过,从而允许重复选择。 接下来,需要给出代码示例。首初始化dp数组为0,然后遍历每个物品,对于每个物品,从它的重量开始到最大容量,逐个更新dp[j]的值。这样就能正确计算完全背包的最大价值。然后解释为什么这样是正确的。因为正序遍历允许重复使用同一物品。比如,当处理物品i时,如果j从小到大更新,那么在计算j的位置时,j-w[i]可能已经被更新过,相当于已经放入过至少一个物品i,所以可以多次选取。 可能需要注意的问题包括初始化是否正确,以及遍历顺序是否正确。例如,如果物品的重量是0的话,可能会导致无限循环,但通常题目中重量是正整数。此外,要确保数组的大小足够,避免越界。 最后,用户可能会提出的相关问题包括一维解法的原理、与0-1背包的区别、时间空间复杂度、初始化问题以及如何处理恰好装满的情况。这些问题都需要在相关问题部分列出,以帮助用户更深入理解。</think>完全背包一维解法通过优化空间复杂度实现,核心在于**正序遍历容量**以支持物品无限次选取。以下是实现代码及原理说明: ```cpp #include <vector> #include <algorithm> using namespace std; int completeKnapsack(int C, vector<int>& weights, vector<int>& values) { vector<int> dp(C + 1, 0); // dp[j]表示容量为j时的最大价值 // 遍历所有物品 for (int i = 0; i < weights.size(); ++i) { int w = weights[i], v = values[i]; // 正序遍历所有可能的容量 for (int j = w; j <= C; ++j) { dp[j] = max(dp[j], dp[j - w] + v); } } return dp[C]; } ``` **原理解释**: 1. **正序遍历的作用**:与0-1背包的逆序遍历不同,正序允许重复选择当前物品。当处理容量`j`时,`dp[j - w]`可能已经包含当前物品的选取,从而支持多次使用。 2. **状态转移方程**:`dp[j] = max(不选当前物品, 选当前物品)`。由于是正序遍历,`dp[j - w]`可能已经被更新过多次。 3. **空间优化**:将二维数组压缩为一维数组,空间复杂度从O(N*C)降低到O(C)。 **示例说明**: 假设物品重量`w=2`,价值`v=5`,背包容量`C=4`: - 初始化:`dp = [0,0,0,0,0]` - 处理第一个物品时: - j=2: `dp[2] = max(0, dp[0]+5) = 5` - j=3: `max(0, dp[1]+5)` 保持0 - j=4: `max(0, dp[2]+5) = 10`
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值