leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 64 】2021-11-12 - 518. 零钱兑换 II #83

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

518. 零钱兑换 II

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/coin-change-2/

前置知识

暂无

题目描述

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例 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
 

注意:

你可以假设:

0 <= amount (总金额) <= 5000
1 <= coin (硬币面额) <= 5000
硬币种类不超过 500 种
结果符合 32 位符号整数
yanglr commented 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数组定义

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];        
    }
};

复杂度分析:

ysy0707 commented 2 years ago

思路:动态规划

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)

ymkymk commented 2 years ago

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];

}

shixinlovey1314 commented 2 years ago

Title:322. Coin Change

Question Reference LeetCode

Solution

Code

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];
    }
};

Complexity

Time Complexity and Explanation

O(n*m)

Space Complexity and Explanation

O(m)

erik7777777 commented 2 years ago

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];
    }
}

Time complexity: O(length of coins * amount) Space complexity: O(amount)

kidexp commented 2 years ago

thoughts

第一想法和coin change类似用memorized search ,但是发现对于同一个amount 比如 3 可以是2+1 也可以是 1+2算了两次

为了避免重复计算,我们amount从前往后按照同一个coin去推算可以避免重复 算完一个coin的组合再算下一个coin 一次进行,这样避免了1+2 和 2+1 计算两次的情况,只有一种会被记录

code

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]

complexity

Time O(amount * n)

Space O(amount)

itsjacob commented 2 years ago

Intuition

Implementation

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];
  }
};

Complexity

Daniel-Zheng commented 2 years ago

思路

动态规划。

代码(C++)

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];
    }
};

复杂度分析

yachtcoder commented 2 years ago

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)
biancaone commented 2 years ago
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]
ZacheryCao commented 2 years ago

Idea:

DP. For coins[0:i], dp[i] the ways of using coins[0:i] to get amount j.

Code:

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)

Complexity:

Time: O(amount * len(coins)) Space: O(amount)

zhangzz2015 commented 2 years ago

思路

关键点

代码

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]; 

    }
};
JiangyanLiNEU commented 2 years ago

Idea

heyqz commented 2 years ago
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)

pan-qin commented 2 years ago

Idea:

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[].

Complexity:

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];
    }
}
wangzehan123 commented 2 years ago

代码

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];
    }
}
shawncvv commented 2 years ago

思路

动态规划

代码

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

florenzliu commented 2 years ago

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:

sxr000511 commented 2 years ago

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]; };

yan0327 commented 2 years ago

思路: 该题是完全背包组合问题,关键捋清=》可重复拿,外物品内背包正序循环,若背包放得下,考虑到组合 则应该是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)

Francis-xsc commented 2 years ago

思路

动态规划

代码


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];
    }
};

复杂度分析

yingliucreates commented 2 years ago

link:

https://leetcode.com/problems/coin-change-2/

代码 Javascript

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];
};
okbug commented 2 years ago
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];
    }
};
chenming-cao commented 2 years ago

解题思路

动态规划。此题为完全背包问题,因为硬币数量没有限制。用一维数组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];
    }
}

复杂度分析

jaysonss commented 2 years ago

思路

完全背包问题,求最大化价值,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)

ghost commented 2 years ago

题目

  1. Coin Change 2

思路

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)

skinnyh commented 2 years ago

Note

Solution

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)

CoreJa commented 2 years ago

思路

标准背包问题,滚动数组,这题实在没想到状态压缩的方法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]
zhy3213 commented 2 years ago

思路

动规,需要注意的一点是消序,这地方卡了我好久

代码

    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)

st2yang commented 2 years ago

思路

代码

复杂度

Moin-Jer commented 2 years ago

思路


动态规划

代码


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];
    }
}

复杂度分析


muimi commented 2 years ago

思路

动态规划、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;
  }
}

复杂度

pangjiadai commented 2 years ago

DP: Python3

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]
shamworld commented 2 years ago
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];
};
cassiechris commented 2 years ago

思路

学习官方题解

代码

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]
pophy commented 2 years ago

思路

Java Code

    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];
    }

时间&空间

hewenyi666 commented 2 years ago

题目名称

518. 零钱兑换 II

题目链接

https://leetcode-cn.com/problems/coin-change-2/

题目思路

转移方程 和经典的完全背包问题的区别在于转移方程式不同: 因为是求多种可能解,则当前amount的组合个数可以由dp[j - coin](选取coin的组合数)和dp[j](不选取coin的,上一个状态的组合数)累积获得 边界:动态规划的边界是dp[0]=1。只有当不选取任何硬币时,金额之和才为 0,因此只有 1 种硬币组合。

code for Python3

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]

复杂度分析

james20141606 commented 2 years ago

Day 64: 518. Coin Change 2 (DP, knapsack)


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]
QiZhongdd commented 2 years ago
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]
};
ai2095 commented 2 years ago

518. Coin Change 2

https://leetcode.com/problems/coin-change-2/

Topics

思路

It's a complete backpack problem.

代码 Python

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)

learning-go123 commented 2 years ago

思路

代码

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 为数组长度。

lilixikun commented 2 years ago

学习官方题解

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];
};
laurallalala commented 2 years ago

代码

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]

复杂度

joeytor commented 2 years ago

思路

压缩空间到一维

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 数组的空间复杂度

shixinlovey1314 commented 2 years ago

Title:518. Coin Change 2

Question Reference LeetCode

Solution

Code

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];
    }
};

Complexity

Time Complexity and Explanation

O(n*m)

Space Complexity and Explanation

O(m)

nonevsnull commented 2 years ago

思路

代码

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)

ychen8777 commented 2 years ago

思路

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)

tongxw commented 2 years ago

思路

朴素思想,把题目中的要求转化为多叉树遍历,比如硬币[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)

leolisgit commented 2 years ago

题目

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)

Zhang6260 commented 2 years ago

JAVA版本

思路:该题是经典的背包问题,动态转移方程为: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];
   }

时间复杂度:O(n*k)

空间复杂度:O(n*k)