算法 - 动规05(Swift版本)

91 阅读3分钟

完全背包理论基础

题目1: 完全背包理论题目

讲解

完全背包的题目与01背包相比 就是每个物品可以取用多次。
在背包容量限制下,装满最大价值的物品。

func bagValue(_ weight: [Int], _ value: [Int], _ bagSize: Int) -> Int {
    guard weight.count > 0, bagSize > 0 else { return 0 }
    var dp = Array(repeating: 0, count: bagSize + 1)
    dp[0] = 0
    for i in 0..<weight.count {
        if bagSize < weight[i] { continue }
        // 重点在这里, 背包从小往大遍历,可以做到物品可以重复使用。
        for j in weight[i]...bagSize {
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
        }
    }
    return dp[bagSize]
}

// let val = bagValue([2, 2, 3, 1, 5, 2], [2, 3, 1, 5, 4, 3], 1) 
// let val = bagValue([1, 3, 4], [15, 20, 30], 4)  // 60
let val = bagValue([1, 2, 3, 4], [2, 4, 4, 5], 5) // 10
print("\(val)")

题目2:518.零钱兑换II

讲解
leetcode

没有AC,应该有值过大 越界问题。 暂未修复。

class Solution {
    func change(_ amount: Int, _ coins: [Int]) -> Int {
        var dp = Array(repeating: 0, count: amount + 1)
        dp[0] = 1
        for i in 0..<coins.count {
            if amount < coins[i] { continue }
            // 顺序遍历 重复取用,完全背包
            for j in coins[i]...amount {
                // 求组合 的递推公式
                dp[j] += dp[j - coins[i]]
            }
        }
        return dp[amount]
    }
}

题目3:377. 组合总和 Ⅳ

讲解
leetcode

手动计算了一下,结果如下图(下面描述 以图标注来讲)。
以2行4列的6 举例:
他的结果来自于两部分:

  1. 不放置物品2的结果, 即1行4列的4。
  2. 放置物品2的结果, 即3行2列的2

解释一下为什么放置物品2的时候, 找的是3行2列的结果?
先解释2列: 背包容量4 - 物品2的重量2 = 2,即去找背包容量为2的结果
再解释3行:
3行即是 因为求排列我们会外层遍历背包,内层遍历物品。
遍历是一列一列的进行,那么进行下一列的时候, 滚动数组dp[j]的结果 就已经被刷新到最后一行, 即第三行了。
所以每个数如果求解放置物品case的时候,如果物品有重量,切换行了,那么都是找的最后一行。
当不放置物品的时候,因为还是在取当前列,dp[j]的结果还是上一行的,并不需要找最后一行。

通过以上的描述:反过来理解下外层遍历背包,内层遍历物品为什么就是求的就是排列数?
最后一行的前置结果,意味着用小容量的背包,装过每一个数字了。
当背包+1 再遍历数字的时候, 就会出现 [1, 3] [3, 1]的不同结果了。

截屏2024-11-22 12.20.58.png

// @lc code=start
class Solution {
    func combinationSum4(_ nums: [Int], _ target: Int) -> Int {
        var dp = Array(repeating: 0, count: target + 1)
        dp[0] = 1
        // 先遍历背包,求排列数目
        // 顺序遍历,可以重复取用
        for j in 0...target {
            print("\n\(j)列:")
            for (i, num) in nums.enumerated() {
                if j >= num {
                   dp[j] += dp[j - num]
                }
                print("第\(i)行: \(dp[j])")
            }
        }
        return dp[target]
    }
}
// @lc code=end

题目4:爬楼梯进阶版

讲解

与组合总和题目一样
属于完全背包种求 排列数量的类型。

func climbingStairs(_ n: Int, _ n: Int) -> Int {
    // 1...m 种爬法, n级台阶
    var dp = Array(repeating: 0, count: n + 1)
    dp[0] = 1
    // 先遍历背包 - 求排列
    // 顺序遍历 - 每种爬法可以重复进行
    for j in 1...n {
        // 1...m种爬法
        for i in 1...m {
            if j >= i {
                dp[j] += dp[j - i]
            }
        }
    }
    return dp[n]
}