「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」
1、题目
给你一个整数数组 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
2、思路
(动态规划,完全背包)
二维分析
状态表示: f[i][j]
表示从前i
种硬币中选,且总金额恰好为j
的所有选法集合的方案数。
那么f[n][amount]
就表示表示从前n
种硬币中选,且总金额恰好为amount
的所有选法集合的方案数,即为答案。
集合划分:
按照第i
种硬币可以选 0
个,1
个,2
个,3
个,,,,k
个划分集合 f[i][j]
。其中k*coin[i] <= j
,也就是说在背包能装下的情况下,枚举第i
种硬币可以选择几个。
- 第
i
种硬币选0
个,f[i][j] = f[i-1][j]
- 第
i
种硬币选1
个,f[i][j] = f[i-1][j - coins[i]]
- 第
i
种硬币选k
个,f[i][j] = f[i-1][j - k*coins[i]]
状态计算:
f[i][j] = f[i-1][j]+f[i-1][j-coins[i]]+f[i-1][j-2*coins[i]],,,,,,+f[i-1][j-k*coins[i]]
。 初始化条件:
f[0][0] = 1
,使用0
种硬币币,凑0
元钱,也是一种方案。
时间复杂度分析: ,其中 是总金额, 是数组 的长度。
3、二维c++代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
int n = coins.size();
vector<vector<int>>f(n + 1, vector<int>(amount + 1 , 0));
f[0][0] = 1; // 使用0种货币,凑0元钱,也是一种方案
for(int i = 1; i <= n; i++)
{
int v =coins[i - 1];
for(int j = 0; j <= amount; j++)
for(int k = 0; k*v <= j; k++)
f[i][j] += f[i-1][j-k*v]; //状态计算方程
}
return f[n][amount];
}
};
复制代码
4、二维java代码
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[][] f = new int[n + 1][amount + 1];
f[0][0] = 1; // 使用0种货币,凑0元钱,也是一种方案
for (int i = 1; i <= n; i++) {
int v = coins[i - 1];
for (int j = 0; j <= amount; j++)
for (int k = 0; k * v <= j; k++)
f[i][j] += f[i - 1][j - k * v]; //状态计算方程
}
return f[n][amount];
}
}
复制代码
5、一维优化
二维完全背包求解方案时间复杂度较高,考虑一维优化。
v
代表第i
种硬币的面值
f[i][j] = f[i-1][j] + f[i-1][j-v]+f[i-1][j-2v]+,,,,,+f[i-1][j-kv])
f[i][j-v] = f[i-1,[j-v]+f[i-1][j-2v]+,,,,,+f[i-1][j-kv])
因此:
f[i][j] = f[i-1][j]+f[i][j-v])
图示:
去掉物品种类维度,状态计算方程为: f[j] = f[j] + f[j-v]
时间复杂度分析: ,其中 是总金额, 是数组 的长度。
6、一维c++代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int>f(amount + 1);
f[0] = 1; //f[0][0] = 1;
for(int i = 1; i <= coins.size(); i++)
{
int v =coins[i - 1];
for(int j = v; j <= amount; j++)
f[j] += f[j - v];
}
return f[amount];
}
};
复制代码
7、一维java代码
class Solution {
public int change(int amount, int[] coins) {
int[] f = new int[amount + 1];
f[0] = 1; //f[0][0] = 1;
for(int i = 1; i <= coins.length; i++)
{
int v =coins[i - 1];
for(int j = v; j <= amount; j++)
f[j] += f[j - v];
}
return f[amount];
}
}
复制代码
原题链接: 518. 零钱兑换 II