完全背包问题---动态规划

什么是完全背包问题?

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。

完全背包问题---动态规划_第1张图片

完全背包的二维解法

递归五部曲:

  1. 确定dp数组以及下标的含义用二维数组 dp[i][j] 表示前 i 个物品在容量 j 时的最大价值,每个物品可以放多次
  2. 确定递推公式:对于 dp[i][j] 来说,要么把第 i 个物品放进去,要么不把第 i 个物品放进去。
    • 如果不放第 i 个物品,dp[i][j] = dp[i-1][j];
    • 如果放入第 i 个物品,因为物品 i 可以放入多次,空出本次物品 i 的空间,剩余空间为 j-weight[i],那么只要这时候 i 个物品在 j-wi 空间里构造出最大价值,即 dp[i][j-weight[i]],再加上此时放入的i的价值vi,就是 dp[i][j] 了,即 dp[i][j] = dp[i][j-weight[i]] + value[i]
    • 注意和 0-1背包问题区分, 01背包中是 dp[ i - 1 ][ j - weight[i]] + value[i])
    • 在放入物品 i 之前,需要判断当前空间 j 是否大于 weight[i]
  3. dp数组初始化:从递推公式中可以看出,dp[i][j] 是由 dp[ i-1 ][j] 和 dp[i][j-weight[i]] 推得的,也就是上面和左面,那么我们只需要初始化第一行;
  4. 确定遍历顺序:先遍历物品还是先遍历背包,其实无论是哪一种,对于 dp[i][j] 上面的和左面的都能得到,但为了更好理解,先遍历物品再遍历背包;
  5. 举例推导dp数组

注意:对于二维数组来说,因为它可以记录所有左边和上边的值,并不会被覆盖,因此只能求解组合问题,而不能求解排列问题。

完全背包的一维解法

就像 0-1背包问题一样,完全背包问题也可以用一维数组解决。从上面可以知道,dp[i][j] 完全依赖于 dp[i-1][j] 和 dp[i][j-weight[i]],也就是上面和左面,因此完全可以一维数组解决。

同时,对于 0-1背包问题,它依赖于上面和左上角,因此左边的数据必须比 dp[i][j] 晚更新,也就是采取倒序的问题,而 完全背包依赖于左边和上边,左边的数据应该更早更新,所以采用正序
0-1背包问题—动态规划

对于一维数组来解决完全背包问题,还存在循环顺序不同会导致排列与组合的差异。即: 先物品后背包是组合问题,先背包后物体是排列问题

举一个例子来说:

leetcode518. 零钱兑换 II

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

如果我们先物品后背包,

  • 外层循环固定硬币种类,比如 coins = [1, 2, 5],那么:
    • 先只考虑 1,计算所有可能的组合。
    • 然后加入 2,计算 1 和 2 的组合。
    • 最后加入 5,计算 1, 2, 5 的组合。
  • ​由于硬币是按顺序处理的,1 永远在 2 前面,2 永远在 5 前面,因此:
    • 1 + 2 会被计算,但 2 + 1 不会被计算(因为 2 的处理在 1 之后)。
    • ​结果:顺序固定,得到的是组合数。

如果先背包再物品,

  • ​外层循环固定金额,比如 j = 5,然后尝试所有硬币:
    • 先尝试 1,计算 dp[5] += dp[4](即 1 + … 的组合)
    • 然后尝试 2,计算 dp[5] += dp[3](即 2 + … 的组合)
    • 最后尝试 5,计算 dp[5] += dp[0](即 5 本身)
  • 由于每次计算 dp[j] 时都会尝试所有硬币,因此:
    • 1 + 2 + 2 和 2 + 1 + 2 会被视为不同的方式。
    • ​结果:顺序不固定,得到的是排列数。

完全背包问题---动态规划_第2张图片
对于初始化问题,dp[0] = 1 即可,不需要初始化第一行或第一列,因为都可以通过第一个数推导出来。

你可能感兴趣的:(算法,动态规划,算法)