70.爬楼梯
链接:LeetCode70.爬楼梯
首先回顾一下动态规划的解法
class Solution {
public:
int climbStairs(int n) {
if(n<=1) return 1;
vector<int> dp(n+1,1);
for(int i=2;i<=n;++i){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
把本题抽象成完全背包问题。
背包体积为n,物品重量为1或者2,物品价值为1.
- 确定dp数组以及下标含义。dp[j]表示爬j阶楼梯到达楼顶的方法个数。
- 确定递推公式公式。dp[j] += dp[j-nums[i]].
- 数组初始化。dp[0]=1;
- 确定遍历顺序。由于求的是排列的个数。故先遍历背包后遍历物品。
- 举例推导dp数组。
class Solution {
public:
int climbStairs(int n) {
vector<int> dp(n+1,0);
dp[0] = 1;
vector<int>nums{1,2};
for(int j=0;j<=n;++j){
for(int i=0;i<2;++i){
if(j>=nums[i]) dp[j] += dp[j-nums[i]];
}
}
return dp[n];
}
};
322.零钱兑换
链接:LeetCode322.零钱兑换
本题可以抽象为完全背包。
背包的体积为amount,物品的重量为coins[i],物品的价值为1.
- 确定dp数组以及下标含义。dp[j]表示凑成金额i所需的最少的硬币个数。
- 确定递推公式公式。dp[j] = min(dp[j],dp[j-coins[i]]+1).
- 数组初始化。由于求的是最小值,故将dp[j]都初始化为INT_MAX,但是由于要进行求和的操作,故将dp[0]初始化为0
- 确定遍历顺序。先遍历背包后遍历物品,或者先遍历物品后遍历背包都是可以的。
- 举例推导dp数组。
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount+1,INT_MAX);
dp[0] = 0;
for(int i=0;i<coins.size();++i){
for(int j=coins[i];j<=amount;++j){
if(dp[j-coins[i]]==INT_MAX) continue;
dp[j]=min(dp[j],dp[j-coins[i]]+1);
}
}
if(dp[amount]==INT_MAX) return -1;
return dp[amount];
}
};
279.完全平方数
链接:LeetCode279.完全平方数
本题可以抽象为完全背包问题:
首先找出小于等于n的完全平方数nums。背包的体积为n,物品的重量为nums[i],价值为1.
- 确定dp数组以及下标含义。dp[j]表示和为i的完全平方数的最少数量。
- 确定递推公式公式。dp[j] = min(dp[j],dp[j-i]+1)其中i为小于等于n的完全平方数
- 数组初始化。由于求的是最小值,故将dp[j]都初始化为INT_MAX,但是由于要进行求和的操作,故将dp[0]初始化为0
- 确定遍历顺序。先遍历背包后遍历物品,或者先遍历物品后遍历背包都是可以的。
- 举例推导dp数组。
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n+1,INT_MAX);
dp[0]=0;
for(int i=1;i<=n;++i){
int m = sqrt(i);
if((m*m)!=i) continue;
for(int j=i;j<=n;++j){
dp[j] = min(dp[j],dp[j-i]+1);
//这里是j-i而不是j-m,i才是完全平方数而不是m
}
}
return dp[n];
}
};
对完全平方数的判断进行改进
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n+1,INT_MAX);
dp[0]=0;
//i*i一定是完全平方数
for(int i=1;i*i<=n;++i){
for(int j=i*i;j<=n;++j){
if(dp[j-i*i]==INT_MAX) continue;//防止溢出
dp[j] = min(dp[j],dp[j-i*i]+1);
}
}
return dp[n];
}
};