● 70. 爬楼梯 (进阶)● 322. 零钱兑换 ● 279.完全平方数

● 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];
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值