零钱兑换Ⅱ 组合总和Ⅳ 爬楼梯(进阶) 零钱兑换

题目1:518 零钱兑换Ⅱ 

题目链接:518 零钱兑换Ⅱ

题意

整数数组coins表示不同面额的硬币,每一种面额的硬币有无限个  amount表示总金额

计算凑成总金额的硬币组合有多少个

若没有组合凑出总金额,返回0

数组中的元素有无限多个,可以无限次放入背包容量为amount的背包,所以可以使用完全背包解决,最终求解  dp[amount]

注意是组合,不是排序

动态规划

动规五部曲

1)dp数组及下标j的含义

dp[j] 表示装满总金额为j的背包,硬币组合数有dp[j]种

2)dp数组初始化

dp[1] = 1

dp[j] = 0

3)递推公式

dp[j] += dp[j-coins[i]]

4)遍历顺序

先遍历物品后遍历背包,遍历了{coins[1],coins[2]}得到的是组合

!!!!先遍历背包后遍历物品,既有{coins[1],coins[2]}  又有{coins[2],coins[1]}得到的是排列

5)打印dp数组

代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        //定义dp数组,初始化dp数组
        vector<int> dp(amount+1, 0);
        dp[0] = 1;
        //遍历顺序
        //先遍历物品后遍历背包
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j] += dp[j-coins[i]];
            }
        }
        for(int i=0;i<=amount;i++) cout<<dp[i]<<endl;
        return dp[amount];
    }
};
  • 时间复杂度: O(mn),其中 m 是amount,n 是 coins 的长度
  • 空间复杂度: O(m)

题目2:组合总和Ⅳ

题目链接:377 组合总和Ⅳ

题意

整数数组nums中元素不同,从nums找出总和为target的元素排列数

数组中的每个元素可以重复使用,装满容量为target的背包,所以可以使用完全背包的思想,最后求解dp[target]

动态规划

动规五部曲

1)确定dp数组及下标j的含义

dp[j]  装满总和为j的背包,有dp[j]种排列方法

2)dp数组初始化

dp[0] = 1

dp[j] = 0

3)递推公式

dp[j] += dp[j-nums[i]]

4)遍历顺序

因为题目要求的是排列数,所以先遍历背包后遍历物品

5)打印dp数组

代码

测试用例有两个数相加超过int的数据,所以需要在if里加上dp[j] < INT_MAX - dp[j - num]

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        //定义dp数组
        vector<int> dp(target+1, 0);
        //初始化dp数组
        dp[0] = 1;
        //遍历顺序
        //先遍历背包后遍历物品
        for(int j=0;j<=target;j++){
            for(int i=0;i<nums.size();i++){
                if(j>=nums[i] && dp[j]<INT_MAX-dp[j-nums[i]]) dp[j] += dp[j-nums[i]];
            }
        }
        return dp[target];
    }
};
  • 时间复杂度: O(target * n),其中 n 为 nums 的长度
  • 空间复杂度: O(target)

题目3:57 爬楼梯(进阶)

题目链接:57 爬楼梯

题意

爬n层才能到达楼顶   每次可以爬至多m个台阶(1<=m<=n) 有多少种方法爬到楼顶

每次都可以使用爬m个阶梯,这相当于物品,可以重复使用;爬到n层才到达楼顶,这是背包容量

完全背包问题,与顺序有关     装满容量为n的背包有几种方法

动态规划

动规五部曲

1)确定dp数组及下标j的含义

dp[j]  爬到j阶楼梯有dp[j]种方法

2)dp数组初始化

dp[0] = 1j

dp[j] = 0

3)递推公式

dp[j] += dp[i-j]

4)遍历顺序

先遍历背包,后遍历物品

5)打印dp数组

代码  (之所以循环不是从j=0,i=0开始,是因为题目中描写的1<=m<=n,每次至少跨一个台阶,所以循环要从1开始遍历)

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    //定义dp数组
    vector<int> dp(n+1, 0);
    //初始化dp数组
    dp[0] = 1;
    //遍历顺序
    for(int j=1;j<=n;j++){
        for(int i=1;i<=m;i++){//m的取值范围是[1,n]
            if(j>=i)   dp[j] += dp[j-i];
        }
    }
   // for(int i=0;i<=n;i++) cout<<dp[i]<<endl;
    cout<<dp[n]<<endl;
}
  • 时间复杂度: O(n * m)   m表示最多可以爬m个台阶
  • 空间复杂度: O(n) 

题目4:322 零钱兑换

题目链接:332 零钱兑换

题意

整数数组coins表示不同面额的硬币,计算可以凑成总和为amount的最小硬币数,若不存在这样的的组合,则返回-1  每种硬币的数量无限

因为题目中要求每种硬币的数量无限个,相当于物品有无限个,凑成容量为amount的背包,背包里面物品个数最少是多少个,最终求解dp[amount]

动态规划

动规五部曲

1)明确dp数组及下标j的含义

dp[j]  凑成金额为j的背包,背包里面硬币个数最少有dp[j]个

2)dp数组初始化

dp[0] = 0   凑成金额为0的背包所需钱币个数一定是0个

dp[j] = INT_MAX

3)递推公式

dp[j] = min(dp[j], dp[j-coins[i]]+1)

4)遍历顺序

因为只要查出最小组合硬币数即可,有无顺序均可

先遍历物品后遍历背包

先遍历背包后遍历物品均可

5)打印dp数组

代码

先遍历物品后遍历背包

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
       //定义dp数组
       vector<int> dp(amount+1, INT_MAX);
       //初始化dp数组
       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) dp[j] = min(dp[j], dp[j-coins[i]]+1);
            // cout<<dp[j]<<endl;
        }
       }
       if(dp[amount]==INT_MAX) return -1;
       return dp[amount];
    }
};
  • 时间复杂度: O(n * amount),其中 n 为 coins 的长度
  • 空间复杂度: O(amount)

先遍历背包后遍历物品

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
       //定义dp数组
       vector<int> dp(amount+1, INT_MAX);
       //初始化dp数组
       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) dp[j] = min(dp[j], dp[j-coins[i]]+1);
    //         // cout<<dp[j]<<endl;
    //     }
    //    }
       //先遍历背包,后遍历物品
       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) dp[j] = min(dp[j], dp[j-coins[i]]+1);
        }
       }
       if(dp[amount]==INT_MAX) return -1;
       return dp[amount];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值