Skip to content

Commit 12d93b3

Browse files
committed
feat: add solutions to lc problem: No.0322
No.0322.Coin Change
1 parent f0d719f commit 12d93b3

File tree

9 files changed

+461
-245
lines changed

9 files changed

+461
-245
lines changed

solution/0300-0399/0322.Coin Change/README.md

+199-92
Original file line numberDiff line numberDiff line change
@@ -50,63 +50,63 @@
5050

5151
**方法一:动态规划**
5252

53-
类似完全背包的思路,硬币数量不限,求凑成总金额所需的最少的硬币个数
53+
我们定义 $f[i][j]$ 表示使用前 $i$ 种硬币,凑出金额 $j$ 的最少硬币数。初始时 $f[0][0] = 0$,其余位置的值均为正无穷
5454

55-
定义 $dp[i][j]$ 表示从前 $i$ 种硬币选出总金额为 $j$ 所需的最少硬币数。
56-
57-
那么有:
55+
我们可以枚举使用的最后一枚硬币的数量 $k$,那么有:
5856

5957
$$
60-
dp[i][j] = \min(dp[i - 1][j], dp[i - 1][j - v] + 1, dp[i - 1][j - 2\times v] + 2, ... , dp[i - 1][j - k\times v] + k)
58+
f[i][j] = \min(f[i - 1][j], f[i - 1][j - x] + 1, \cdots, f[i - 1][j - k \times x] + k)
6159
$$
6260

63-
令 $j=j-v$,则有:
61+
其中 $x$ 表示第 $i$ 种硬币的面值。
62+
63+
不妨令 $j = j - x$,那么有:
6464

6565
$$
66-
dp[i][j - v] = \min( dp[i - 1][j - v], dp[i - 1][j - 2\times v] + 1, ... , dp[i - 1][j - k\times v] + k - 1)
66+
f[i][j - x] = \min(f[i - 1][j - x], f[i - 1][j - 2 \times x] + 1, \cdots, f[i - 1][j - k \times x] + k - 1)
6767
$$
6868

69-
因此,我们可以得到状态转移方程
69+
将二式代入一式,我们可以得到以下状态转移方程
7070

7171
$$
72-
dp[i][j] = \min(dp[i - 1][j], dp[i][j - v] + 1)
72+
f[i][j] = \min(f[i - 1][j], f[i][j - x] + 1)
7373
$$
7474

75-
时间复杂度 $O(m\times n)$,空间复杂度 $O(m\times n)$。其中 $m$ 和 $n$ 分别为硬币数量和总金额。
75+
最后答案即为 $f[m][n]$。
76+
77+
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为硬币的种类数和总金额。
78+
79+
注意到 $f[i][j]$ 只与 $f[i - 1][j]$ 和 $f[i][j - x]$ 有关,因此我们可以将二维数组优化为一维数组,空间复杂度降为 $O(n)$。
7680

7781
<!-- tabs:start -->
7882

7983
### **Python3**
8084

8185
<!-- 这里可写当前语言的特殊实现逻辑 -->
8286

83-
动态规划——完全背包问题朴素做法:
84-
8587
```python
8688
class Solution:
8789
def coinChange(self, coins: List[int], amount: int) -> int:
8890
m, n = len(coins), amount
89-
dp = [[n + 1] * (n + 1) for _ in range(m + 1)]
90-
dp[0][0] = 0
91-
for i in range(1, m + 1):
91+
f = [[inf] * (n + 1) for _ in range(m + 1)]
92+
f[0][0] = 0
93+
for i, x in enumerate(coins, 1):
9294
for j in range(n + 1):
93-
dp[i][j] = dp[i - 1][j]
94-
if j >= coins[i - 1]:
95-
dp[i][j] = min(dp[i][j], dp[i][j - coins[i - 1]] + 1)
96-
return -1 if dp[-1][-1] > n else dp[-1][-1]
95+
f[i][j] = f[i - 1][j]
96+
if j >= x:
97+
f[i][j] = min(f[i][j], f[i][j - x] + 1)
98+
return -1 if f[m][n] >= inf else f[m][n]
9799
```
98100

99-
动态规划——完全背包问题空间优化:
100-
101101
```python
102102
class Solution:
103103
def coinChange(self, coins: List[int], amount: int) -> int:
104-
dp = [amount + 1] * (amount + 1)
105-
dp[0] = 0
106-
for coin in coins:
107-
for j in range(coin, amount + 1):
108-
dp[j] = min(dp[j], dp[j - coin] + 1)
109-
return -1 if dp[-1] > amount else dp[-1]
104+
n = amount
105+
f = [0] + [inf] * n
106+
for x in coins:
107+
for j in range(x, n + 1):
108+
f[j] = min(f[j], f[j - x] + 1)
109+
return -1 if f[n] >= inf else f[n]
110110
```
111111

112112
### **Java**
@@ -116,74 +116,82 @@ class Solution:
116116
```java
117117
class Solution {
118118
public int coinChange(int[] coins, int amount) {
119+
final int inf = 1 << 30;
119120
int m = coins.length;
120-
int[][] dp = new int[m + 1][amount + 1];
121-
for (int i = 0; i <= m; ++i) {
122-
Arrays.fill(dp[i], amount + 1);
121+
int n = amount;
122+
int[][] f = new int[m + 1][n + 1];
123+
for (var g : f) {
124+
Arrays.fill(g, inf);
123125
}
124-
dp[0][0] = 0;
126+
f[0][0] = 0;
125127
for (int i = 1; i <= m; ++i) {
126-
int v = coins[i - 1];
127-
for (int j = 0; j <= amount; ++j) {
128-
dp[i][j] = dp[i - 1][j];
129-
if (j >= v) {
130-
dp[i][j] = Math.min(dp[i][j], dp[i][j - v] + 1);
128+
for (int j = 0; j <= n; ++j) {
129+
f[i][j] = f[i - 1][j];
130+
if (j >= coins[i - 1]) {
131+
f[i][j] = Math.min(f[i][j], f[i][j - coins[i - 1]] + 1);
131132
}
132133
}
133134
}
134-
return dp[m][amount] > amount ? -1 : dp[m][amount];
135+
return f[m][n] >= inf ? -1 : f[m][n];
135136
}
136137
}
137138
```
138139

139140
```java
140141
class Solution {
141142
public int coinChange(int[] coins, int amount) {
142-
int[] dp = new int[amount + 1];
143-
Arrays.fill(dp, amount + 1);
144-
dp[0] = 0;
145-
for (int coin : coins) {
146-
for (int j = coin; j <= amount; j++) {
147-
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
143+
final int inf = 1 << 30;
144+
int n = amount;
145+
int[] f = new int[n + 1];
146+
Arrays.fill(f, inf);
147+
f[0] = 0;
148+
for (int x : coins) {
149+
for (int j = x; j <= n; ++j) {
150+
f[j] = Math.min(f[j], f[j - x] + 1);
148151
}
149152
}
150-
return dp[amount] > amount ? -1 : dp[amount];
153+
return f[n] >= inf ? -1 : f[n];
151154
}
152155
}
153156
```
154157

155-
### **JavaScript**
158+
### **C++**
156159

157-
```js
158-
/**
159-
* @param {number[]} coins
160-
* @param {number} amount
161-
* @return {number}
162-
*/
163-
var coinChange = function (coins, amount) {
164-
let dp = Array(amount + 1).fill(amount + 1);
165-
dp[0] = 0;
166-
for (const coin of coins) {
167-
for (let j = coin; j <= amount; ++j) {
168-
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
160+
```cpp
161+
class Solution {
162+
public:
163+
int coinChange(vector<int>& coins, int amount) {
164+
int m = coins.size(), n = amount;
165+
int f[m + 1][n + 1];
166+
memset(f, 0x3f, sizeof(f));
167+
f[0][0] = 0;
168+
for (int i = 1; i <= m; ++i) {
169+
for (int j = 0; j <= n; ++j) {
170+
f[i][j] = f[i - 1][j];
171+
if (j >= coins[i - 1]) {
172+
f[i][j] = min(f[i][j], f[i][j - coins[i - 1]] + 1);
173+
}
174+
}
169175
}
176+
return f[m][n] > n ? -1 : f[m][n];
170177
}
171-
return dp[amount] > amount ? -1 : dp[amount];
172178
};
173179
```
174180
175-
### **C++**
176-
177181
```cpp
178182
class Solution {
179183
public:
180184
int coinChange(vector<int>& coins, int amount) {
181-
vector<int> dp(amount + 1, amount + 1);
182-
dp[0] = 0;
183-
for (auto& coin : coins)
184-
for (int j = coin; j <= amount; ++j)
185-
dp[j] = min(dp[j], dp[j - coin] + 1);
186-
return dp[amount] > amount ? -1 : dp[amount];
185+
int n = amount;
186+
int f[n + 1];
187+
memset(f, 0x3f, sizeof(f));
188+
f[0] = 0;
189+
for (int x : coins) {
190+
for (int j = x; j <= n; ++j) {
191+
f[j] = min(f[j], f[j - x] + 1);
192+
}
193+
}
194+
return f[n] > n ? -1 : f[n];
187195
}
188196
};
189197
```
@@ -192,19 +200,55 @@ public:
192200

193201
```go
194202
func coinChange(coins []int, amount int) int {
195-
dp := make([]int, amount+1)
196-
for i := 1; i <= amount; i++ {
197-
dp[i] = amount + 1
203+
m, n := len(coins), amount
204+
f := make([][]int, m+1)
205+
const inf = 1 << 30
206+
for i := range f {
207+
f[i] = make([]int, n+1)
208+
for j := range f[i] {
209+
f[i][j] = inf
210+
}
211+
}
212+
f[0][0] = 0
213+
for i := 1; i <= m; i++ {
214+
for j := 0; j <= n; j++ {
215+
f[i][j] = f[i-1][j]
216+
if j >= coins[i-1] {
217+
f[i][j] = min(f[i][j], f[i][j-coins[i-1]]+1)
218+
}
219+
}
220+
}
221+
if f[m][n] > n {
222+
return -1
223+
}
224+
return f[m][n]
225+
}
226+
227+
func min(a, b int) int {
228+
if a < b {
229+
return a
230+
}
231+
return b
232+
}
233+
```
234+
235+
```go
236+
func coinChange(coins []int, amount int) int {
237+
n := amount
238+
f := make([]int, n+1)
239+
for i := range f {
240+
f[i] = 1 << 30
198241
}
199-
for _, coin := range coins {
200-
for j := coin; j <= amount; j++ {
201-
dp[j] = min(dp[j], dp[j-coin]+1)
242+
f[0] = 0
243+
for _, x := range coins {
244+
for j := x; j <= n; j++ {
245+
f[j] = min(f[j], f[j-x]+1)
202246
}
203247
}
204-
if dp[amount] > amount {
248+
if f[n] > n {
205249
return -1
206250
}
207-
return dp[amount]
251+
return f[n]
208252
}
209253

210254
func min(a, b int) int {
@@ -215,18 +259,85 @@ func min(a, b int) int {
215259
}
216260
```
217261

262+
### **JavaScript**
263+
264+
```js
265+
/**
266+
* @param {number[]} coins
267+
* @param {number} amount
268+
* @return {number}
269+
*/
270+
var coinChange = function (coins, amount) {
271+
const m = coins.length;
272+
const n = amount;
273+
const f = Array(m + 1)
274+
.fill(0)
275+
.map(() => Array(n + 1).fill(1 << 30));
276+
f[0][0] = 0;
277+
for (let i = 1; i <= m; ++i) {
278+
for (let j = 0; j <= n; ++j) {
279+
f[i][j] = f[i - 1][j];
280+
if (j >= coins[i - 1]) {
281+
f[i][j] = Math.min(f[i][j], f[i][j - coins[i - 1]] + 1);
282+
}
283+
}
284+
}
285+
return f[m][n] > n ? -1 : f[m][n];
286+
};
287+
```
288+
289+
```js
290+
/**
291+
* @param {number[]} coins
292+
* @param {number} amount
293+
* @return {number}
294+
*/
295+
var coinChange = function (coins, amount) {
296+
const n = amount;
297+
const f = Array(n + 1).fill(1 << 30);
298+
f[0] = 0;
299+
for (const x of coins) {
300+
for (let j = x; j <= n; ++j) {
301+
f[j] = Math.min(f[j], f[j - x] + 1);
302+
}
303+
}
304+
return f[n] > n ? -1 : f[n];
305+
};
306+
```
307+
218308
### **TypeScript**
219309

220310
```ts
221311
function coinChange(coins: number[], amount: number): number {
222-
let dp = new Array(amount + 1).fill(amount + 1);
223-
dp[0] = 0;
224-
for (const coin of coins) {
225-
for (let j = coin; j <= amount; ++j) {
226-
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
312+
const m = coins.length;
313+
const n = amount;
314+
const f: number[][] = Array(m + 1)
315+
.fill(0)
316+
.map(() => Array(n + 1).fill(1 << 30));
317+
f[0][0] = 0;
318+
for (let i = 1; i <= m; ++i) {
319+
for (let j = 0; j <= n; ++j) {
320+
f[i][j] = f[i - 1][j];
321+
if (j >= coins[i - 1]) {
322+
f[i][j] = Math.min(f[i][j], f[i][j - coins[i - 1]] + 1);
323+
}
324+
}
325+
}
326+
return f[m][n] > n ? -1 : f[m][n];
327+
}
328+
```
329+
330+
```ts
331+
function coinChange(coins: number[], amount: number): number {
332+
const n = amount;
333+
const f: number[] = Array(n + 1).fill(1 << 30);
334+
f[0] = 0;
335+
for (const x of coins) {
336+
for (let j = x; j <= n; ++j) {
337+
f[j] = Math.min(f[j], f[j - x] + 1);
227338
}
228339
}
229-
return dp[amount] > amount ? -1 : dp[amount];
340+
return f[n] > n ? -1 : f[n];
230341
}
231342
```
232343

@@ -235,22 +346,18 @@ function coinChange(coins: number[], amount: number): number {
235346
```rust
236347
impl Solution {
237348
pub fn coin_change(coins: Vec<i32>, amount: i32) -> i32 {
238-
let n = coins.len();
239-
let amount = amount as usize;
240-
let mut dp = vec![amount + 1; amount + 1];
241-
dp[0] = 0;
242-
for i in 1..=amount {
243-
for j in 0..n {
244-
let coin = coins[j] as usize;
245-
if coin <= i {
246-
dp[i] = dp[i].min(dp[i - coin] + 1);
247-
}
349+
let n = amount as usize;
350+
let mut f = vec![n + 1; n + 1];
351+
f[0] = 0;
352+
for &x in &coins {
353+
for j in x as usize..=n {
354+
f[j] = f[j].min(f[j - x as usize] + 1);
248355
}
249356
}
250-
if dp[amount] > amount {
357+
if f[n] > n {
251358
-1
252359
} else {
253-
dp[amount] as i32
360+
f[n] as i32
254361
}
255362
}
256363
}

0 commit comments

Comments
 (0)