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
思路当然,下面是基于您提供的代码重新整理的解题思路:
把可走的步数当成不同质量的物品
- 确定dp数组(dp table)以及下标的含义
定义一个数组
dp,其中dp[i]表示到达第i阶楼梯的不同方法数量。 - 确定递推公式
由于每次可以爬 1 或 2 个台阶,因此到达第
i阶的方法数量是到达第i-1阶和第i-2阶的方法数量之和。具体的递推公式为:
这个递推公式在代码中以
dp[i] += dp[i - j] 的形式出现,其中 j 取 1 和 2。
- dp数组如何初始化
dp[0]应初始化为 1,因为到达第 0 阶有一种方法,即不爬。 - 确定遍历顺序
从
dp[1]开始,一直到dp[n],每个dp[i]都是基于前两个状态(dp[i-1]和dp[i-2])来计算的。 - 举例推导dp数组
以
n = 4为例,dp[0] = 1, dp[1] = 1(初始化)
dp[2] = dp[1] + dp[0] = 1 + 1 = 2dp[3] = dp[2] + dp[1] = 2 + 1 = 3dp[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
思路
可以当成一个完全背包问题
- 确定dp数组(dp table)以及下标的含义
我们定义一个数组
dp,其中dp[i]表示凑成金额i所需的最少硬币个数。 - 确定递推公式
要凑成金额
i,我们可以选择硬币的面值j(其中j属于coins数组),然后dp[i]就等于dp[i-j] + 1。我们要从所有可能的j中选择一个使dp[i]最小的。
具体的递推公式为:
- dp数组如何初始化
dp[0]应该初始化为 0,因为凑成金额 0 所需的硬币数量是 0。其他的dp[i]应该初始化为一个大数(比如INT_MAX),作为无法凑成的标记。 - 确定遍历顺序
从
dp[1]开始,一直到dp[amount],确保每次计算dp[i]时,dp[i-j]已经被计算过。 - 举例推导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) = 2dp[4] = min(dp[4], dp[4-1] + 1) = min(dp[4], dp[3] + 1) = 2dp[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
思路
- 确定dp数组(dp table)以及下标的含义
定义一个数组
dp,其中dp[i]表示和为i的完全平方数的最少数量。 - 确定递推公式
要凑成和为
i,我们可以选择一个完全平方数j^2(j为整数),然后dp[i]就等于dp[i - j^2] + 1。我们要从所有可能的j^2中选择一个使dp[i]最小的。
具体的递推公式为:
其中,(j^2) 是小于等于 (i) 的完全平方数。
- dp数组如何初始化
dp[0]应该初始化为 0,因为和为 0 的完全平方数的数量是 0。其他的dp[i]应该初始化为一个大数(例如INT_MAX),作为无法凑成的标记。 - 确定遍历顺序
从
dp[1]开始,一直到dp[n],确保每次计算dp[i]时,dp[i - j^2]已经被计算过。 - 举例推导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) = 2dp[3] = min(dp[3], dp[3-1] + 1) = min(dp[3], dp[2] + 1) = 3dp[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];
}
};