LeetCode第 518 题:零钱兑换 II(C++)

518. 零钱兑换 II - 力扣(LeetCode)

注意本题与组合数LeetCode第 377 题:组合总数 IV(C++)_zj-CSDN博客的区别,这题求的是组合数,顺序无关。

经典动态规划:完全背包问题 - labuladong的算法小抄

本题是典型的完全背包问题,将amount视为背包容量,coins数组视为物品重量数组即可。我们要求的是装法数(组合数),与顺序无关(与顺序相关的我们叫排列数)。

第一步,首先看状态选择:状态显然有两个,一个是背包容量,一个是可选择的物品;选择就是对应该物品装/不装。

dp的典型框架:

LeetCode第 518 题:零钱兑换 II(C++)_第1张图片

第二步,需要明确dp数组的含义

dp[i][j]表示只用前i件物品,能够装满容量j的装法种数(个人感这一步才是最难理解的

第三步,寻找状态转移方程,也就是明确状态的转移逻辑。

转移逻辑就是对不同的选择进行分类讨论:

  • 选择不装:也就是不使用当前硬币coins[i],那么能够凑出面额j的方法数量dp[i][j]应该等于dp[i-1][j],也就是继承之前的结果;
  • 选择装:那么此时装入coins[i]进去之后容量刚好为j,也就是使用当前硬币coins[i],那么dp[i][j] = dp[i][j-coins[i-1]] (i从1开始,所以这儿会-1)。
    这儿的dp[i][j-coins[i-1]]表示,只用前i(包含)个物品能够凑出容量j-coins[i-1]的种数,此时往这些情况里在加一个第i个物品,不就只使用前i个物品凑出了容量j - conis[i-1] + coins[i-1]= j了吗?

另外注意,本题求的是组合数,所以上面两种情况应该进行累加方法的数量。

第四步,翻译成代码,处理好数组初始化就可以了:

class Solution {
public:
    int change(int amount, vector& coins) {
        int n = coins.size();
        //dp[i][j]表示只用前i件物品,能够装满容量j的装法种数
        vector> dp(n+1, vector(amount+1, 0));
        for(int i = 0; i <= n; ++i) dp[i][0] = 1;//容量为0的时候不装就是一种装法
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= amount; ++j){
                if(coins[i-1] <= j){//面值(重量)小于等于容量才装的下
                    //装得下的时候,可以选择不装/装
                    dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
                }else dp[i][j] = dp[i-1][j];//装不下的时候装法数只能等于上一个状态的
            }
        }
        return dp[n][amount];
    }
};

状态压缩:

class Solution {
public:
    int change(int amount, vector& coins) {
        int n = coins.size();
        //dp[j]能够装满容量j的装法种数
        vector dp(amount+1, 0);
        dp[0] = 1;//容量为0的时候不装就是一种装法
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= amount; ++j){
                if(coins[i-1] <= j){//面值(重量)小于等于容量才装的下
                    //装得下的时候,可以选择不装/装
                    dp[j] = dp[j] + dp[j-coins[i-1]];
                }else dp[j] = dp[j];//装不下的时候装法数只能等于上一个状态的
            }
        }
        return dp[amount];
    }
};

优化一下:

class Solution {
public:
    int change(int amount, vector& coins) {
        int n = coins.size();
        //dp[j]能够装满容量j的装法种数
        vector dp(amount+1, 0);
        dp[0] = 1;//容量为0的时候不装就是一种装法
        for(int i = 0; i < n; ++i){
            for(int j = 1; j <= amount; ++j){
                if(coins[i] <= j){//面值(重量)小于等于容量才装的下
                    //装得下的时候,可以选择不装/装
                    dp[j] = dp[j] + dp[j-coins[i]];
                }
            }
        }
        return dp[amount];
    }
};

你可能感兴趣的:(数据结构与算法,leetcode,动态规划)