Open azl397985856 opened 2 years ago
这个题属于背包问题的类似问题, 每种面值的硬币都有选或不选两种选择。
顺着题目的要求定义dp数组:
dp[i]: 凑成金额i 的总的组合方式的总数量。
对于coins = [1, 2, 5], amount = 7
如果用dp数组表示要求的数, 就可以转换为求dp[7]
如果先用掉1个,
dp[7-1] = dp[6],
故走这条路径, 可以贡献出的组合方式数量: dp[i] = dp[6] * 1
dp[7-2]} = dp[5],
如果走这条路径, 可以贡献出的组合方式数量为: dp[5]
dp[7-5] = dp[2],
如果走这条路径, 可以贡献出的组合方式数量为: dp[2]
相加可得: dp[6] + dp[5] + dp[2]
递推公式为:
dp[i] = Σ dp[i - coin[i]] (coin[i] ∈ coins)
dp数组定义
dp[i]: 凑成金额 i 的组合方式的总数量
实现语言: C++
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp(amount + 1); // dp[i]: 凑成金额 i 的组合方式的总数量
dp[0] = 1; // 可以使用一个tets case测试出来
for (int& coin : coins)
{
for (int i = coin; i <= amount; i++)
{
dp[i] = dp[i - coin] + dp[i]; /* 当前面值的硬币, 如果选它接下来处理总金额 i-coin, 如果不选它继续处理总金额i */
}
}
return dp[amount];
}
};
思路:动态规划
class Solution {
public int change(int amount, int[] coins) {
int len = coins.length;
int[][] dp = new int[len + 1][amount + 1];
//初始化面额为0的时候都是有1种方式(就是全都不选)
for(int i = 0; i <= len; i++){
dp[i][0] = 1;
}
for(int j = 1; j <= amount; j++){
for(int i = 1; i <= len; i++){
//这里注意第i种硬币面值在coins数组中应为下标i - 1
int val = coins[i - 1];
for(int n = 0; n * val <=j; n++){
dp[i][j] = dp[i][j] + dp[i - 1][j - n * val];
}
}
}
return dp[len][amount];
}
}
时间复杂度:O(N^2) 空间复杂度:O(N^2)
int coinChange(int[] coins, int amount) { int[] dp = new int[amount + 1]; // 数组大小为 amount + 1,初始值也为 amount + 1 Arrays.fill(dp, amount + 1);
// base case
dp[0] = 0;
// 外层 for 循环在遍历所有状态的所有取值
for (int i = 0; i < dp.length; i++) {
// 内层 for 循环在求所有选择的最小值
for (int coin : coins) {
// 子问题无解,跳过
if (i - coin < 0) {
continue;
}
dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
}
}
return (dp[amount] == amount + 1) ? -1 : dp[amount];
}
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i <= amount; i++)
for (int j = 0; j < coins.size(); j++)
if (coins[j] <= i && dp[i-coins[j]] != INT_MAX)
dp[i] = min(dp[i], dp[i-coins[j]] + 1);
return dp[amount] == INT_MAX? -1 : dp[amount];
}
};
O(n*m)
O(m)
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int x = coin; x <= amount;x++) {
dp[x] += dp[x - coin];
}
}
return dp[amount];
}
}
第一想法和coin change类似用memorized search ,但是发现对于同一个amount 比如 3 可以是2+1 也可以是 1+2算了两次
为了避免重复计算,我们amount从前往后按照同一个coin去推算可以避免重复 算完一个coin的组合再算下一个coin 一次进行,这样避免了1+2 和 2+1 计算两次的情况,只有一种会被记录
from collections import defaultdict
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
if amount == 0:
return 0
dp = defaultdict(int)
dp[0] = 1
for coin in coins:
for val in range(coin, amount + 1):
dp[val] += dp[val - coin]
return dp[amount]
Time O(amount * n)
Space O(amount)
class Solution
{
public:
int change(int amount, vector<int> &coins)
{
std::vector<int> dp(amount + 1, 0);
dp[0] = 1;
// coin is the outer loop, meaning where can this coin contribute to?
for (auto const &coin : coins) {
for (int s = 0; s <= amount; s++) {
if (s + coin > amount) continue;
dp[s + coin] += dp[s];
}
}
return dp[amount];
}
};
动态规划。
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp(amount + 1);
dp[0] = 1;
for (int& coin : coins) for (int i = coin; i <= amount; i++) dp[i] += dp[i - coin];
return dp[amount];
}
};
dfs+memo O(nS), O(nS)
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
N = len(coins)
@cache
def dfs(idx, csum):
if csum == amount:
return 1
if idx == N or csum > amount:
return 0
return dfs(idx, csum + coins[idx]) + dfs(idx+1, csum)
return dfs(0, 0)
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for i in coins:
for j in range(1, amount + 1):
if j >= i:
dp[j] += dp[j - i]
return dp[amount]
DP. For coins[0:i], dp[i] the ways of using coins[0:i] to get amount j.
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp={}
dp[0] = 1
for i in coins:
for j in range(i, amount + 1):
if j not in dp:
dp[j] = dp[j-i]
else:
dp[j] += dp[j-i]
return dp.get(amount, 0)
Time: O(amount * len(coins)) Space: O(amount)
c Code:
class Solution {
public:
int change(int amount, vector<int>& coins) {
// define
// dp(x ,y ) = dp(x - coins[y], y) + dp(x, y-1)
unordered_map<int, unordered_map<int,int>> mem;
return dfs(amount, coins, coins.size()-1, mem);
}
int dfs(int amount, vector<int>& coins, int index, unordered_map<int, unordered_map<int,int>>& mem)
{
if(amount<0)
return 0;
if(amount==0)
return 1;
if(mem.find(amount) != mem.end() && mem[amount].find(index)!=mem[amount].end())
return mem[amount][index];
int total =0;
for(int i=0; i<=index; i++)
{
total +=dfs(amount - coins[i], coins, i, mem);
}
mem[amount][index] = total;
return total;
}
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
// define
// dp(x ,y ) = dp(x - coins[y], y) + dp(x, y-1)
vector<vector<int>> dpTable(coins.size()+1, vector<int>(amount+1, 0));
for(int i=0; i< coins.size(); i++)
{
for(int j=0; j <=amount; j++)
{
if(j==0)
dpTable[i+1][j] = 1;
else
{
if(j - coins[i]>=0)
dpTable[i+1][j] = (dpTable[i+1][j-coins[i]] + dpTable[i][j]);
else
dpTable[i+1][j] = dpTable[i][j];
}
}
}
return dpTable[coins.size()][amount];
}
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
// define
// dp(x ,y ) = dp(x - coins[y], y) + dp(x, y-1)
vector<int> dpTable(amount+1, 0);
for(int i=0; i< coins.size(); i++)
{
for(int j=0; j <=amount; j++)
{
if(j==0)
dpTable[j] = 1;
else
{
if(j - coins[i]>=0)
dpTable[j] = (dpTable[j-coins[i]] + dpTable[j]);
}
}
}
return dpTable[amount];
}
};
var change = function(amount, coins) {
let dp = Array(coins.length).fill().map(() => Array(amount+1).fill(0));
dp[0][0] = 1;
for (let j=1; j<=amount; j++){j%coins[0] == 0 ? dp[0][j] = 1 : dp[0][j] = 0; }; // done with row
for (let i=0; i<coins.length;i++){dp[i][0] = 1}; //done with column
for (let i=1; i<coins.length; i++){
for (let j=1; j<=amount; j++){
dp[i][j] = dp[i-1][j] + (coins[i] > j ? 0 : dp[i][j-coins[i]])
};
};
return dp[coins.length-1][amount];
};
class Solution(object):
def change(self, amount, coins):
dp = [[0 for i in range(amount+1)] for j in range(len(coins))]
for i in range(len(coins)):
dp[i][0] = 1
for j in range(1, amount+1):
dp[0][j] = 0 if j%coins[0]!=0 else 1
for i in range(1, len(coins)):
for j in range(1, amount+1):
dp[i][j] = dp[i-1][j] + (0 if j<coins[i] else dp[i][j-coins[i]])
return dp[-1][-1]
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for coin in coins:
for i in range(coin, amount + 1):
dp[i] += dp[i - coin]
return dp[amount]
time complexity: O(mn) space complexity: O(m)
same as coin change 1. DP. dp[i][j] store the number of ways to make amount j using coin[0]...coin[i]. Here we optimize dp[][] to 1d dp[].
Time: O(amount*n) Space: O(amount)
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
// int[][] dp = new int [n+1][amount+1];
int[] dp = new int[amount+1];
// dp[0][0]=1;
dp[0]=1;
for(int i=0;i<n;i++) {
int val = coins[i];
for(int j=val;j<=amount;j++) {
//dp[i][j]=dp[i-1][j];
//dp[j]=dp[j];
// if(j-val>=0)
dp[j]+=dp[j-val];
/*
for(int k=1;k*val<=j;k++) {
dp[i][j]+=dp[i-1][j-k*val];
}
*/
}
}
// return dp[n][amount];
return dp[amount];
}
}
Java Code:
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}
动态规划
var change = function (amount, coins) {
const dp = Array.from({ length: amount + 1 }).fill(0);
dp[0] = 1;
for (let coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
};
复杂度
令n是coins的数量, m是amount
Explanation
Python
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0 for _ in range(amount+1)]
dp[0] = 1
for c in coins:
for i in range(c, amount+1):
dp[i] += dp[i-c]
return dp[-1]
Complexity:
O(mn)
where m is the amount and n is the length of the coins listO(m)
var change = function(amount, coins) { let dp = Array(coins.length).fill().map(() => Array(amount+1).fill(0)); dp[0][0] = 1; for (let j=1; j<=amount; j++){j%coins[0] == 0 ? dp[0][j] = 1 : dp[0][j] = 0; }; // done with row for (let i=0; i<coins.length;i++){dp[i][0] = 1}; //done with column for (let i=1; i<coins.length; i++){ for (let j=1; j<=amount; j++){ dp[i][j] = dp[i-1][j] + (coins[i] > j ? 0 : dp[i][j-coins[i]]) }; }; return dp[coins.length-1][amount]; };
思路: 该题是完全背包组合问题,关键捋清=》可重复拿,外物品内背包正序循环,若背包放得下,考虑到组合 则应该是dp[i] += dp[i-coin] 初始dp[0]= 1 因为本来也是一个组合
func change(amount int, coins []int) int {
dp := make([]int,amount+1)
dp[0] = 1
for _,coin := range coins{
for i:=0;i<=amount;i++{
if i>=coin{
dp[i] += dp[i-coin]
}
}
}
return dp[amount]
}
时间复杂度:O(nm)其中m为amount的长度+1 空间复杂度:O(m)
动态规划
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int>dp(amount+1,0);
dp[0]=1;
for(int coin:coins){
for(int i=coin;i<=amount;i++){
dp[i]+=dp[i-coin];
}
}
return dp[amount];
}
};
复杂度分析
https://leetcode.com/problems/coin-change-2/
const change = function (amount, coins) {
let dp = [];
for (let row = 0; row <= coins.length; row++) {
let array = [];
for (let col = 0; col <= amount; col++) array.push(-1);
dp.push(array);
}
for (let ctr = 0; ctr <= coins.length; ctr++) dp[ctr][0] = 1;
for (let ctr = 1; ctr <= amount; ctr++) dp[0][ctr] = 0;
for (let row = 1; row <= coins.length; row++)
for (let col = 1; col <= amount; col++) {
if (coins[row - 1] <= col)
dp[row][col] = dp[row][col - coins[row - 1]] + dp[row - 1][col];
else dp[row][col] = dp[row - 1][col];
}
return dp[coins.length][amount];
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> f(mount + 1);
f[0] = 1;
for (auto x: coins) {
for (int i = x; i <= amount; i++) {
f[i] += f[i - x];
}
}
return f[amount];
}
};
动态规划。此题为完全背包问题,因为硬币数量没有限制。用一维数组dp[i]表示达到amount = i的不同方法数。状态转移方程是dp[i] = dp[i] + dp[i - coin],起始状态为dp[0] = 1,amount = 0时一个硬币也不取即可,此为1种方法。注意我们遍历i的时候是从小到大遍历的,不会出现重复计算的情况。
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
// base case: amount = 0, no coins selected, dp[0] = 1
dp[0] = 1;
for (int coin : coins) {
// iterate i in increasing order, will not have repeated cases
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}
复杂度分析
完全背包问题,求最大化价值,dp[0]=1
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] + dp[j-coins[i]];
}
}
return dp[amount];
}
}
时间复杂度:O(n * amount) 空间复杂度:O(amount)
DP
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount+1)
dp[0] = 1
for coin in coins:
for i in range(coin, amount+1):
dp[i] += dp[i-coin]
return dp[amount]
Space: O(amount) Time: O(amount*n)
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
# the outer loop need to be coins to avoid duplicate combinations
for c in coins:
for i in range(amount + 1):
if c <= i:
dp[i] += dp[i - c]
return dp[amount]
Time complexity: O(M*N) M=amount, N=len(coins)
Space complexity: O(M)
标准背包问题,滚动数组,这题实在没想到状态压缩的方法2333
方程如下:
dp[j] = dp[j - coins[i]] + dp[j]
初始化为dp=[1]+[0]*amount
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
coins.sort(reverse=True)
dp = [1] + [0] * amount
for i in range(len(coins)):
for j in range(coins[i], amount + 1):
dp[j] = dp[j - coins[i]] + dp[j]
return dp[-1]
动规,需要注意的一点是消序,这地方卡了我好久
def change(self, amount: int, coins: List[int]) -> int:
if not amount:
return 1
dp=[0]*(amount+1)
dp[0]=1
for i in coins:
for j in range(i,amount+1):
dp[j]+=dp[j-i]
return dp[-1]
时间O(n*amount) 空间O(amount)
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for coin in coins:
for i in range(coin, amount + 1):
dp[i] += dp[i - coin]
return dp[amount]
动态规划
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}
动态规划、DFS
class Solution {
public int change(int amount, int[] coins) {
return dfs(coins, 0, amount, new Integer[coins.length][amount + 1]);
}
private int dfs(int[] coins, int index, int amount, Integer[][] dp) {
if (amount == 0) return 1;
if (index == coins.length) return 0;
if (dp[index][amount] != null) return dp[index][amount];
int ret = dfs(coins, index + 1, amount, dp);
if (amount >= coins[index]) ret += dfs(coins, index , amount - coins[index], dp);
return dp[index][amount] = ret;
}
}
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for coin in coins:
for x in range(coin, amount + 1):
dp[x] += dp[x - coin]
return dp[amount]
var change = function(amount, coins) {
let dp = Array(amount+1).fill(0);
dp[0] = 1;
for(let coin of coins){
for(let i = coin; i <= amount; i++){
dp[i] += dp[i-coin];
}
}
return dp[amount];
};
学习官方题解
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for j in range(len(coins)):
for i in range(1, amount + 1):
if i >= coins[j]:
dp[i] += dp[i - coins[j]]
return dp[-1]
public int change(int amount, int[] coins) {
int n = coins.length;
int[][] dp = new int[n+1][amount+1];
for (int i = 1; i <= n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= amount; j++) {
dp[i][j] = dp[i-1][j];
int coin = coins[i-1];
if (j - coin >= 0) {
dp[i][j] += dp[i][j - coin] ;
}
}
}
return dp[n][amount];
}
518. 零钱兑换 II
https://leetcode-cn.com/problems/coin-change-2/
转移方程 和经典的完全背包问题的区别在于转移方程式不同: 因为是求多种可能解,则当前amount的组合个数可以由dp[j - coin](选取coin的组合数)和dp[j](不选取coin的,上一个状态的组合数)累积获得 边界:动态规划的边界是dp[0]=1。只有当不选取任何硬币时,金额之和才为 0,因此只有 1 种硬币组合。
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
n = len(coins)
dp = [0] * (amount + 1)
dp[0] = 1
for i in range(1, n + 1):
coin = coins[i - 1]
for j in range(coin, amount + 1, 1):
dp[j] = dp[j - coin] + dp[j]
return dp[amount]
Problem Link
Ideas
dp[i][j] = dp[i-1][j] + dp[i][j - coins[i]]
, which means choosing or not choosing coin[i]
. Then we could also write as 1D form.Complexity: hash table and bucket
Code
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount+1)
dp[0] = 1
for coin in coins:
for i in range(1, amount+1):
if i >= coin:
dp[i] += dp[i - coin]
return dp[-1]
var change = function(amount, coins) {
const dp = Array.from({ length: amount + 1 }).fill(0);
dp[0]=1;
for(let coin of coins){
for(let i=coin;i<=amount;i++){
dp[i]+=dp[i-coin]
}
}
return dp[amount]
};
https://leetcode.com/problems/coin-change-2/
It's a complete backpack problem.
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0 for _ in range(amount+1)]
dp[0] = 1
for i in range(len(coins)):
for j in range(coins[i], amount+1):
dp[j] = dp[j] + dp[j-coins[i]]
return dp[amount]
DP 时间复杂度: O(amount*len(coins)) 空间复杂度:O(amount)
Go Code:
func change(amount int, coins []int) int {
dp := make([]int, amount+1)
dp[0] = 1
for _, coin := range coins {
for i := coin; i <= amount; i++ {
dp[i] += dp[i-coin]
}
}
return dp[amount]
}
复杂度分析
令 n 为数组长度。
学习官方题解
var change = function(amount, coins) {
const dp = new Array(amount + 1).fill(0);
dp[0] = 1;
for (const coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
};
class Solution(object):
def change(self, amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
dp = [0]*(amount+1)
dp[0] = 1
for c in coins:
for i in range(c, amount+1):
if i-c>=0:
dp[i] += dp[i-c]
return dp[amount]
压缩空间到一维
dp[j]
只是用前 i 种 coin, 有多少种方法可以实现 amount j, 压缩空间到 一维, 每次循环时的 dp 数组代表只是用 前 i 种 coin
base case
dp[0] = 1
动态转移
因为是完全背包, 每个 coin 有无限次数可以使用, 所以正向遍历 [1, amount+1]
如果 i-c ≥ 0, 那么更新 dp[i] += dp[i-c]
dp[i] 本身包含了 只使用 前 j-1 种 coin 达到 amount i 的方式
现在 加上 dp[i-c] 代表了使用前 j 种 coin (包括这个 coin 本身, 因为在之前遍历到的 i-c 的时候已经将使用这个 coin 的情况加入其中了) 达到 amount i 的方
return
dp[-1] 返回 使用所有 coin 能有多少方法实现 amount
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0 for _ in range(amount+1)]
dp[0] = 1
for c in coins:
for i in range(1, amount+1):
if i-c >= 0:
dp[i] += dp[i-c]
return dp[-1]
时间复杂度: O(n * amount) 两重循环的时间复杂度
空间复杂度: O(amount) dp 数组的空间复杂度
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp(amount + 1, 0);
dp[0] = 1;
for (int coin : coins)
for (int x = coin; x <= amount; x++)
dp[x] += dp[x - coin];
return dp[amount];
}
};
O(n*m)
O(m)
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for(int j = 0;j < coins.length;j++){
for(int i = coins[j];i <= amount;i++){
if(i >= coins[j]) dp[i] += dp[i - coins[j]];
}
}
return dp[amount];
}
}
time: O(amount * coins.length) space: O(amount)
dp
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount+1];
dp[0] = 1;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}
时间: O (amount * coins.length) \ 空间: O(amount)
朴素思想,把题目中的要求转化为多叉树遍历,比如硬币[1, 2]
,组成金额3
最后算叶子节点为0的个数。
这里有个问题是会重复计算,例如3-(-1)-2-(-2)-0和3-(-2)-1-(-1)-0的结果是一样的,都是选硬币1和2。这里只需要组合数,不需要排列数。所以需要针对排列数剪枝。这个思想类似与LC 39 组合求和,选过的数字,在下一层的第二个分支不能重复选。
由于需要记忆化递归,记忆数组里除了需要记住节点值对应的方案数,还需要记住这个节点是属于哪个分支的,也就是所选取的硬币数字。
public int change(int amount, int[] coins) {
Integer[][] memo = new Integer[coins.length + 1][amount + 1];
return dfs(0, amount, coins, memo);
}
private int dfs(int start, int left, int[] coins, Integer[][] memo) {
if (left == 0) {
return 1;
}
if (memo[start][left] != null) {
return memo[start][left];
}
int count = 0;
for (int i=start; i<coins.length; i++) {
if (left - coins[i] >= 0) {
count += dfs(i, left - coins[i], coins, memo);
}
}
memo[start][left] = count;
return count;
}
TC: O(len(coins) amount) SC: O(len(coins) amount)
https://leetcode.com/problems/coin-change-2/
完全背包。每种组合只能在结果中出现一次。dp[i]这里对状态进行了压缩。
其实应该是dp[i][j], 前i个硬币组成数额为j的组合数。
dp[i][j] = dp[i][j - coins[1]] + dp[i][j - coins[2]] + ... + dp[i][j - coins[n]];
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];
}
}
时间:O(n^2)
空间:O(n)
思路:该题是经典的背包问题,动态转移方程为:dp[i] [j]=dp[i] [j-1]+dp[i-coins[j-1]] [j]],(该题未使用内存压缩,)
public int change(int amount, int[] coins) { //使用动态规划 非压缩的内存的形式 Arrays.sort(coins); int dp[][]=new int[amount+1][coins.length+1]; Arrays.fill(dp[0],1); for(int i=1;i<=coins.length;i++){ for(int j=coins[0];j<=amount;j++){ if(j>=coins[i-1]){ dp[j][i]=dp[j-coins[i-1]][i]+dp[j][i-1]; }else{ dp[j][i] = dp[j][i-1]; } } } return dp[amount][coins.length]; }
518. 零钱兑换 II
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/coin-change-2/
前置知识
暂无
题目描述
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例 1: