力扣第518题 零钱兑换II c++附java代码+打印dp代码 完全背包题型

题目

518. 零钱兑换 II

中等

相关标签

数组   动态规划

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

输入:amount = 10, coins = [10] 
输出:1

提示:

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • coins 中的所有值 互不相同
  • 0 <= amount <= 5000

思路和解题方法

  1. 首先,创建一个大小为 amount+1 的数组 dp,并将所有元素初始化为0。这个数组用于存储不同金额的组合数。
  2. 接下来,将 dp[0](金额为0时的组合数)设置为1,表示当金额为0时,只有一种组合方式,即不选择任何硬币。
  3. 然后,进入两个嵌套的循环。外层循环遍历硬币数组 coins,内层循环遍历金额范围 j,从硬币的面值开始,直到目标金额 amount
  4. 在循环中,我们通过累加 dp[j]dp[j-coins[i]] 的值来更新 dp[j]。其中 dp[j] 表示当前金额 j 的组合数,dp[j-coins[i]] 表示在不选取当前硬币情况下,剩余金额为 j-coins[i] 的组合数。
  5. 通过这样的更新方式,我们实际上是在计算不同金额下的组合数,并利用之前计算好的结果进行累加计算。最终,dp[amount] 就保存了总金额为 amount 时的组合数。
  6. 最后,返回 dp[amount] 的值,即为所求的结果。

复杂度

        时间复杂度:

                O(amount * n)

        时间复杂度是O(amount * n),其中amount是目标金额,n是硬币的数量。这是因为有两个嵌套循环,外层循环在硬币数组上进行迭代,内层循环在金额范围上进行迭代。

        空间复杂度

                O(amount)

        空间复杂度是O(amount),因为我们创建了一个大小为amount+1的dp数组来存储不同金额的组合数。这个数组的长度与目标金额相关,因此空间复杂度与目标金额成线性关系。

c++ 代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0); // 创建一个长度为amount+1的动态规划数组dp,初始值全部设为0
        dp[0]=1; // 将dp[0]初始化为1,表示组成金额为0的方法数为1
        for(int i = 0;i<coins.size();i++) // 遍历物品(硬币)
        {
            for(int j = coins[i];j<=amount;j++) // 遍历背包(金额)
            {
                dp[j] += dp[j-coins[i]]; // 使用动态规划更新dp数组,累加上一个硬币面额之前的方法数
            }
        }
            
        return  dp[amount]; // 返回dp[amount],即组成目标金额的方法数
    }
};

JAVA 代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0); // 创建一个长度为amount+1的动态规划数组dp,初始值全部设为0
        dp[0]=1; // 将dp[0]初始化为1,表示组成金额为0的方法数为1
        for(int i = 0;i<coins.size();i++) // 遍历物品(硬币)
        {
            for(int j = coins[i];j<=amount;j++) // 遍历背包(金额)
            {
                dp[j] += dp[j-coins[i]]; // 使用动态规划更新dp数组,累加上一个硬币面额之前的方法数
            }
        }
            
        return  dp[amount]; // 返回dp[amount],即组成目标金额的方法数
    }
};

手写流程

附上测试数据

c++

#include <vector>
#include<iostream>
using namespace std;

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        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 k : dp)
                cout << k << "  ";
            cout << endl;
        }
        return dp[amount];
    }
};

int main() {
    int amount = 5;
    vector<int> coins = { 1, 2, 5 };

    Solution s;
    int result = s.change(amount, coins);

    cout << result << endl; // 输出 4

    return 0;
}

java

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int amount = 5;
        int[] coins = {1, 2, 5};
        int result = solution.change(amount, coins);
        System.out.println("Number of Ways: " + result);

        // 输出dp数组
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        System.out.println("DP Array:");
        for (int i = 0; i <= amount; i++) {
            System.out.print(dp[i] + " ");
        }
        System.out.println();
    }
}

易理解的二维数组

// 二维dp数组版本,方便理解
class Solution {
    public int change(int amount, int[] coins) {
        int[][] dp = new int[coins.length][amount + 1];
        // 只有一种硬币的情况
        for (int i = 0; i <= amount; i += coins[0]) {
            dp[0][i] = 1;
        }
        for (int i = 1; i < coins.length; i++) {
            for (int j = 0; j <= amount; j++) {
                // 第i种硬币使用0~k次,求和
                for (int k = 0; k * coins[i] <= j; k++) {
                    dp[i][j] += dp[i - 1][j - k * coins[i]];
                }
            }
        }
        return dp[coins.length - 1][amount];
    }
}

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值