50
50
51
51
** 方法一:动态规划**
52
52
53
- 类似完全背包的思路,硬币数量不限,求凑成总金额所需的最少的硬币个数 。
53
+ 我们定义 $f [ i ] [ j ] $ 表示使用前 $i$ 种硬币,凑出金额 $j$ 的最少硬币数。初始时 $f [ 0 ] [ 0 ] = 0$,其余位置的值均为正无穷 。
54
54
55
- 定义 $dp[ i] [ j ] $ 表示从前 $i$ 种硬币选出总金额为 $j$ 所需的最少硬币数。
56
-
57
- 那么有:
55
+ 我们可以枚举使用的最后一枚硬币的数量 $k$,那么有:
58
56
59
57
$$
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)
61
59
$$
62
60
63
- 令 $j=j-v$,则有:
61
+ 其中 $x$ 表示第 $i$ 种硬币的面值。
62
+
63
+ 不妨令 $j = j - x$,那么有:
64
64
65
65
$$
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)
67
67
$$
68
68
69
- 因此,我们可以得到状态转移方程 :
69
+ 将二式代入一式,我们可以得到以下状态转移方程 :
70
70
71
71
$$
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)
73
73
$$
74
74
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)$。
76
80
77
81
<!-- tabs:start -->
78
82
79
83
### ** Python3**
80
84
81
85
<!-- 这里可写当前语言的特殊实现逻辑 -->
82
86
83
- 动态规划——完全背包问题朴素做法:
84
-
85
87
``` python
86
88
class Solution :
87
89
def coinChange (self , coins : List[int ], amount : int ) -> int :
88
90
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 ):
92
94
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 ]
97
99
```
98
100
99
- 动态规划——完全背包问题空间优化:
100
-
101
101
``` python
102
102
class Solution :
103
103
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 ]
110
110
```
111
111
112
112
### ** Java**
@@ -116,74 +116,82 @@ class Solution:
116
116
``` java
117
117
class Solution {
118
118
public int coinChange (int [] coins , int amount ) {
119
+ final int inf = 1 << 30 ;
119
120
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);
123
125
}
124
- dp [0 ][0 ] = 0 ;
126
+ f [0 ][0 ] = 0 ;
125
127
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 );
131
132
}
132
133
}
133
134
}
134
- return dp [m][amount ] > amount ? - 1 : dp [m][amount ];
135
+ return f [m][n ] >= inf ? - 1 : f [m][n ];
135
136
}
136
137
}
137
138
```
138
139
139
140
``` java
140
141
class Solution {
141
142
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 );
148
151
}
149
152
}
150
- return dp[amount ] > amount ? - 1 : dp[amount ];
153
+ return f[n ] >= inf ? - 1 : f[n ];
151
154
}
152
155
}
153
156
```
154
157
155
- ### ** JavaScript **
158
+ ### ** C++ **
156
159
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
+ }
169
175
}
176
+ return f[ m] [ n ] > n ? -1 : f[ m] [ n ] ;
170
177
}
171
- return dp[amount] > amount ? - 1 : dp[amount];
172
178
};
173
179
```
174
180
175
- ### ** C++**
176
-
177
181
```cpp
178
182
class Solution {
179
183
public:
180
184
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];
187
195
}
188
196
};
189
197
```
@@ -192,19 +200,55 @@ public:
192
200
193
201
``` go
194
202
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
198
241
}
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 )
202
246
}
203
247
}
204
- if dp[amount ] > amount {
248
+ if f[n ] > n {
205
249
return -1
206
250
}
207
- return dp[amount ]
251
+ return f[n ]
208
252
}
209
253
210
254
func min (a , b int ) int {
@@ -215,18 +259,85 @@ func min(a, b int) int {
215
259
}
216
260
```
217
261
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
+
218
308
### ** TypeScript**
219
309
220
310
``` ts
221
311
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 );
227
338
}
228
339
}
229
- return dp [ amount ] > amount ? - 1 : dp [ amount ];
340
+ return f [ n ] > n ? - 1 : f [ n ];
230
341
}
231
342
```
232
343
@@ -235,22 +346,18 @@ function coinChange(coins: number[], amount: number): number {
235
346
``` rust
236
347
impl Solution {
237
348
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 );
248
355
}
249
356
}
250
- if dp [ amount ] > amount {
357
+ if f [ n ] > n {
251
358
- 1
252
359
} else {
253
- dp [ amount ] as i32
360
+ f [ n ] as i32
254
361
}
255
362
}
256
363
}
0 commit comments