力扣第518题 零钱兑换II c++附java代码+打印dp代码 完全背包题型
题目
中等
相关标签
给你一个整数数组 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
思路和解题方法
- 首先,创建一个大小为
amount+1
的数组dp
,并将所有元素初始化为0。这个数组用于存储不同金额的组合数。- 接下来,将
dp[0]
(金额为0时的组合数)设置为1,表示当金额为0时,只有一种组合方式,即不选择任何硬币。- 然后,进入两个嵌套的循环。外层循环遍历硬币数组
coins
,内层循环遍历金额范围j
,从硬币的面值开始,直到目标金额amount
。- 在循环中,我们通过累加
dp[j]
和dp[j-coins[i]]
的值来更新dp[j]
。其中dp[j]
表示当前金额j
的组合数,dp[j-coins[i]]
表示在不选取当前硬币情况下,剩余金额为j-coins[i]
的组合数。- 通过这样的更新方式,我们实际上是在计算不同金额下的组合数,并利用之前计算好的结果进行累加计算。最终,
dp[amount]
就保存了总金额为amount
时的组合数。- 最后,返回
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];
}
}
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)