day37 动态规划05

84 阅读3分钟

完全背包

文章讲解

思路:

-   **初始化:**

    -   当容量为0时,无论什么物品都放不下,所以 `dp[i][0]` 都是0。
    -   只考虑物品0时,如果背包容量足够,就一直放物品0,直到背包装不下。

-   **递推公式:**

    -   对于 `dp[i][j]`,我们有两种选择:

        -   **不放物品 `i`:**  那么最大价值就是 `dp[i-1][j]`,也就是只考虑前 `i-1` 种物品,背包子容量为 `j` 的最大价值。
        -   **放物品 `i`:**  那么我们需要先给物品 `i` 腾出背包空间,剩余容量是 `j - weight[i]`。此时,我们继续考虑前 `i` 种物品,背包容量为 `j - weight[i]` 的最大价值,再加上物品 `i` 的价值 `value[i]`    -   取两种情况的最大值:`dp[i][j] = max(dp[i-1][j], dp[i][j - weight[i]] + value[i])`
def complete_backpack_n_plus_1(n, bagWeight, weight, value):
    """
    完全背包问题,物品数量为 n,求最大价值

    Args:
        n: 物品数量
        bagWeight: 背包最大容量
        weight: 每种物品的重量列表
        value: 每种物品的价值列表

    Returns:
        最大价值
    """

    # 1. 创建表格 (n 行)
    dp = [[0 for _ in range(bagWeight + 1)] for _ in range(n)]

    # 2. 初始化
    for j in range(weight[0], bagWeight + 1):
        dp[0][j] = dp[0][j - weight[0]] + value[0]

    # 3. 填写表格
    for i in range(1, n):
        for j in range(bagWeight + 1):
            if j < weight[i]:
                dp[i][j] = dp[i - 1][j]
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])

    # 4. 返回答案
    return dp[n - 1][bagWeight]


n, bagWeight = map(int, input().split())
weight = []
value = []
for _ in range(n):
    w, v = map(int, input().split())
    weight.append(w)
    value.append(v)

result = complete_backpack_n_plus_1(n, bagWeight, weight, value)
print(result)

518. 零钱兑换 II

文章讲解

思路:

硬币无限,完全背包问题(遍历背包容量时候不需要逆序)。dp[j]表示能凑出金额j的方法数。

注意:凑金额0时有一种方法,什么硬币都不放。
class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        """
        计算凑出总金额的硬币组合数。

        Args:
            amount: 总金额。
            coins: 硬币面值列表。

        Returns:
            凑出总金额的组合数。
        """
        dp = [0 ] * (amount + 1 ) # dp[j]表示凑出金额j的方法数
        dp[0] = 1 # 金额为0 不用凑即可

        # 遍历硬币
        for coin in coins:
            # 顺序遍历容量(完全背包,重复取没关系,不需要逆序)
            for j in range(coin, amount+1):
                dp[j] +=  dp[j - coin]
        return dp[amount]

377. 组合总和 Ⅳ

文章讲解

思路:


class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        '''求dp[i][j]目标为target,前i个元素的组合数量(实际上是排列,排列要先迭代容量)
        dp[i][j] = dp[i-1][j] + dp[i][j-nums[i-1]]
        => dp[j] = dp[j] + dp[j-nums[i-1]]
        '''
        n = len(nums)
        dp = [0] * (target+1)

        # 初始化 target=0时候只有空集算 注意!
        dp[0] = 1

        # 先容量再元素(排列问题,顺序不同是新的组合,先遍历容量,再内容)
        for j in range(1, target+1):
            for num in nums:
                if j >= num:
                    dp[j] += dp[j - num]
        return dp[target]

57. 爬楼梯

文章讲解

思路:

dp[i]表示爬到有i个台阶的楼梯,有dp[i]种方法
对于j<=i,dp[i]+=dp[i-j] j∈[1,m]'

物品是每次可以爬的步数1-m,容量是楼梯的台阶数。物品可以无限取,完全背包问题,背包容量顺序遍历;
由于21 12爬的方法不同,是排列问题,先迭代容量,再迭代物品。
n,m = list(map(int, input().split()))


def climbing_stais(n, m):
    dp = [0] * (n+1)
    
    dp[0] = 1 # 方法的话0=1;硬币数量=1
    
    for i in range(1, n+1):
        for j in range(1,m+1):
            if i >= j:
                dp[i] += dp[i-j]
    return dp[n]
    
print(climbing_stais(n,m))

image.png