● 70. 爬楼梯 (进阶)
题目:57. 爬楼梯
题目描述:
根据示例:
可知1到m的阶数可以重复选择,跳了1阶之后还能跳一阶,所以是完全背包,又因为考虑了顺序问题,所以是完全背包的排列数问题。其实就是和昨天● 377. 组合总和 Ⅳ 一样的做法,我们要组合的和target(背包容量)就是到达楼顶的n阶,物品就是1到m。
在动态规划:494.目标和 (opens new window) 、 动态规划:518.零钱兑换II (opens new window)、动态规划:377. 组合总和 Ⅳ (opens new window)中,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];
代码如下:
#include<iostream>
#include<vector>
using namespace std;
void countMethod(){
//多少种排列数,加起来的和target是n;nums数组是1到m
int n,m;
cin>>n>>m;
vector<int> dp(n+1,0);
dp[0]=1;
for(int j=0;j<=n;++j){
for(int i=1;i<=m;++i){
if(j>=i)dp[j]+=dp[j-i];
}
}
cout<<dp[n];
}
int main()
{
countMethod();
return 0;
}
● 322. 零钱兑换
动规五部曲:
1.DP数组及其下标的含义。dp[j]:凑成金额j最少需要dp[j]个硬币。
2.递推公式。这里和之前不一样,是求最小重量。到现在我们的递推公式已经涉及最大最小价值、排列数、组合数了。
排列组合数使用的是求和公式来统计,最大最小价值就需要考虑放与不放两种情况,然后取max/min值,才能对应最大最小价值。
所以不存放硬币i的时候,凑成现在背包里面金额为j-coins[i]的最少硬币数是dp[j-coins[i]]。因为是求最少硬币数,所以应该比较dp[j](是前j-1轮的最少硬币数)和放入硬币i之后的最少硬币数dp[j-coins[i]]+1,的最小值。
所以递推公式为:dp[j]=min(dp[j],dp[j-coins[i]+1)。
3.DP数组如何初始化。dp[0]肯定是=0。但是其他元素不能和之前一样初始化为0,想想之前最大价值初始化为0是为了不在取max的时候覆盖非0值,那么现在应该取INT_MAX,保证比过程中生成的元素值都大,就不会在取min的时候覆盖他们。
4.遍历顺序。因为元素可以重复取,所以背包应该从左往右遍历。和之前的排列组合不同,这里求的是排列组合中的一个最少的情况,所以物品(硬币)有顺序没有顺序都可以,即先硬币还是先背包都可以。
5.打印DP数组。
输入:coins = [1, 2, 5], amount = 5。
代码如下。然后还要注意有两种情况:总金额amount等于0没有硬币可以组成直接返回0;dp[amount]=初始值说明没有硬币能组成amount直接返回-1。
另外,在min中,dp[j-coins[i]]+1如果dp[j-coins[i]]是INT_MAX,再加1会超出范围,所以要在前面加个条件,等于初始值的话就说明不能组成j-coin[i],跳过。
代码如下:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
if(amount==0)return 0;
vector<int> dp(amount+1,INT_MAX);
dp[0]=0;
for(int j=0;j<=amount;++j){//先背包
for(int i=0;i<coins.size();++i){
if(j>=coins[i] && dp[j-coins[i]]<INT_MAX-1){
dp[j]=min(dp[j-coins[i]]+1,dp[j]);}
}
}
if(dp[amount]==INT_MAX)return -1;
return dp[amount];
}
};
● 279.完全平方数
1.递推公式和dp[j]定义。和上一道一样也是求最少数量,所以dp[j]和递推公式还是一样的,取min值。
2.遍历顺序。只不过不是多个数加起来,而是多个数的平方加起来。所以物品的重量应该是 1,4,9,……,n ,最大设置为 n 。所以遍历物品的时候,i的取值应该是1,2,……,根号n,考虑有的数自己就是一个平方数,所以 根号n 需要取到。
发现是完全背包问题,所以背包的遍历顺序从小到大。
3.初始化。同样的,n是0的话数量肯定是0,在初始化dp[0]之前先把所有元素①初始化为INT_MAX,在循环中的语句前面就要加if条件。②初始化为INT_MAX-1,就不用家条件,因为:
dp[n]肯定比n小,所以初始化的值大于n的最大值10^4就是正确的。
4.打印dp数组。
代码如下:
class Solution {
public:
int numSquares(int n) {
//物品是1,4,9,……,根号n。闭区间
vector<int> dp(n+1,INT_MAX-1);
dp[0]=0;
for(int i=1;i<=sqrt(n);++i){
for(int j=i*i;j<=n;++j){ //重量是i的平方
dp[j]=min(dp[j],dp[j-i*i]+1); //最少数量,两种情况
}
}
return dp[n];
}
};