LeetCode Day48

67 阅读4分钟

70. 爬楼梯(背包解法)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1: 输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶 示例 2: 输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶

提示:

  • 1 <= n <= 45

思路当然,下面是基于您提供的代码重新整理的解题思路:

把可走的步数当成不同质量的物品

  1. 确定dp数组(dp table)以及下标的含义 定义一个数组 dp,其中 dp[i] 表示到达第 i 阶楼梯的不同方法数量。
  2. 确定递推公式 由于每次可以爬 1 或 2 个台阶,因此到达第 i 阶的方法数量是到达第 i-1 阶和第 i-2 阶的方法数量之和。具体的递推公式为:

这个递推公式在代码中以 dp[i] += dp[i - j] 的形式出现,其中 j 取 1 和 2。

  1. dp数组如何初始化 dp[0] 应初始化为 1,因为到达第 0 阶有一种方法,即不爬。
  2. 确定遍历顺序dp[1] 开始,一直到 dp[n],每个 dp[i] 都是基于前两个状态(dp[i-1]dp[i-2])来计算的。
  3. 举例推导dp数组n = 4 为例,dp[0] = 1, dp[1] = 1(初始化)
  • dp[2] = dp[1] + dp[0] = 1 + 1 = 2
  • dp[3] = dp[2] + dp[1] = 2 + 1 = 3
  • dp[4] = dp[3] + dp[2] = 3 + 2 = 5

最后,dp[n] 即为到达第 n 阶的不同方法数量。

题解

class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n + 1, 0);
        dp[0] = 1;  // 初始化
        int m = 2;  // 可以爬的最大步数(1 或 2)
        
        for (int i = 1; i <= n; i++) {  // 遍历“背包”,即目标楼梯层数
            for (int j = 1; j <= m; j++) {  // 遍历“物品”,即选择的步数
                if (i - j >= 0) {
                    dp[i] += dp[i - j];  // 递推公式
                }
            }
        }
        return dp[n];
    }
};

322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。

示例 1: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1 示例 2: 输入:coins = [2], amount = 3 输出:-1 示例 3: 输入:coins = [1], amount = 0 输出:0

思路

可以当成一个完全背包问题

  1. 确定dp数组(dp table)以及下标的含义 我们定义一个数组 dp,其中 dp[i] 表示凑成金额 i 所需的最少硬币个数。
  2. 确定递推公式 要凑成金额 i,我们可以选择硬币的面值 j(其中 j 属于 coins 数组),然后 dp[i] 就等于 dp[i-j] + 1。我们要从所有可能的 j 中选择一个使 dp[i] 最小的。

具体的递推公式为:

  1. dp数组如何初始化 dp[0] 应该初始化为 0,因为凑成金额 0 所需的硬币数量是 0。其他的 dp[i] 应该初始化为一个大数(比如 INT_MAX),作为无法凑成的标记。
  2. 确定遍历顺序dp[1] 开始,一直到 dp[amount],确保每次计算 dp[i] 时,dp[i-j] 已经被计算过。
  3. 举例推导dp数组coins = [1, 2, 5], amount = 11 为例,dp[0] = 0, dp[1] = 1, dp[2] = 1, dp[5] = 1(初始化)
  • dp[3] = min(dp[3], dp[3-1] + 1) = min(dp[3], dp[2] + 1) = 2
  • dp[4] = min(dp[4], dp[4-1] + 1) = min(dp[4], dp[3] + 1) = 2
  • dp[6] = min(dp[6], dp[6-5] + 1) = min(dp[6], dp[1] + 1) = 2
  • ...
  • dp[11] = min(dp[11], dp[11-5] + 1) = min(dp[11], dp[6] + 1) = 3

最终,dp[amount] 就是我们要求的答案。如果 dp[amount] 还是初始值,则返回 -1。

题解

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= amount; ++i) {
            for (int coin : coins) {
                if (i - coin < 0 || dp[i - coin] == INT_MAX) continue;
                dp[i] = min(dp[i], dp[i - coin] + 1);
            }
        }
        return dp[amount] == INT_MAX ? -1 : dp[amount];
    }
};

279. 完全平方数

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1: 输入:n = 12 输出:3 解释:12 = 4 + 4 + 4 示例 2: 输入:n = 13 输出:2 解释:13 = 4 + 9

思路

  1. 确定dp数组(dp table)以及下标的含义 定义一个数组 dp,其中 dp[i] 表示和为 i 的完全平方数的最少数量。
  2. 确定递推公式 要凑成和为 i,我们可以选择一个完全平方数 j^2j 为整数),然后 dp[i] 就等于 dp[i - j^2] + 1。我们要从所有可能的 j^2 中选择一个使 dp[i] 最小的。

具体的递推公式为: 其中,(j^2) 是小于等于 (i) 的完全平方数。

  1. dp数组如何初始化 dp[0] 应该初始化为 0,因为和为 0 的完全平方数的数量是 0。其他的 dp[i] 应该初始化为一个大数(例如 INT_MAX),作为无法凑成的标记。
  2. 确定遍历顺序dp[1] 开始,一直到 dp[n],确保每次计算 dp[i] 时,dp[i - j^2] 已经被计算过。
  3. 举例推导dp数组n = 12 为例,dp[0] = 0, dp[1] = 1, dp[4] = 1, dp[9] = 1(初始化)
  • dp[2] = min(dp[2], dp[2-1] + 1) = min(dp[2], dp[1] + 1) = 2
  • dp[3] = min(dp[3], dp[3-1] + 1) = min(dp[3], dp[2] + 1) = 3
  • dp[5] = min(dp[5], dp[5-4] + 1) = min(dp[5], dp[1] + 1) = 2
  • ...
  • dp[12] = min(dp[12], dp[12-4] + 1) = min(dp[12], dp[8] + 1) = 3

最终,dp[n] 就是我们要求的答案。

题解

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j * j <= i; ++j) {
                if (dp[i - j * j] != INT_MAX) {
                    dp[i] = min(dp[i], dp[i - j * j] + 1);
                }
            }
        }
        return dp[n];
    }
};