Skip to content

Commit ff0e5c2

Browse files
committed
三刷322
1 parent 0517935 commit ff0e5c2

File tree

3 files changed

+133
-36
lines changed

3 files changed

+133
-36
lines changed

Diff for: docs/0322-coin-change.adoc

+70-36
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
11
[#0322-coin-change]
2-
= 322. Coin Change
2+
= 322. 零钱兑换
33

4-
{leetcode}/problems/coin-change/[LeetCode - Coin Change^]
4+
https://leetcode.cn/problems/coin-change/[LeetCode - 322. 零钱兑换 ^]
55

6-
image::images/0322-01.png[{image_attr}]
6+
给你一个整数数组 `coins`,表示不同面额的硬币;以及一个整数 `amount`,表示总金额。
77

8-
思考题:
8+
计算并返回可以凑成总金额所需的 *最少的硬币个数*。如果没有任何一种硬币组合能组成总金额,返回 `-1`
99

10-
. 如何把动态规划代码写得更简洁一些?
11-
. 如何使用自底向上方法来实现一遍动态规划?
10+
你可以认为每种硬币的数量是无限的。
1211

13-
You are given coins of different denominations and a total amount of money _amount_. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return `-1`.
12+
*示例 1:*
1413

15-
*Example 1:*
14+
....
15+
输入:coins = [1, 2, 5], amount = 11
16+
输出:3
17+
解释:11 = 5 + 5 + 1
18+
....
1619

17-
[subs="verbatim,quotes,macros"]
18-
----
19-
*Input:* coins = `[1, 2, 5]`, amount = `11`
20-
*Output:* `3`
21-
*Explanation:* 11 = 5 + 5 + 1
22-
----
20+
*示例 2:*
2321

24-
*Example 2:*
22+
....
23+
输入:coins = [2], amount = 3
24+
输出:-1
25+
....
2526

26-
[subs="verbatim,quotes,macros"]
27-
----
28-
*Input:* coins = `[2]`, amount = `3`
29-
*Output:* -1
27+
*示例 3:*
3028

31-
----
29+
....
30+
输入:coins = [1], amount = 0
31+
输出:0
32+
....
3233

33-
*Note*:
34+
*提示:*
3435

36+
* `+1 <= coins.length <= 12+`
37+
* `1 \<= coins[i] \<= 2^31^ - 1`
38+
* `0 \<= amount \<= 10^4^`
3539
36-
You may assume that you have an infinite number of each kind of coin.
3740
41+
[#思路分析]
3842
== 思路分析
3943

4044
解题思路
@@ -43,17 +47,7 @@ You may assume that you have an infinite number of each kind of coin.
4347
. 而在回溯法中,如果含有很多的重复的计算的时候,就可以使用记忆化的搜索,将可能出现的重复计算大状态使用一个数组来保存其值,在进行重复的计算的时候,就可以直接的调用数组中的值,较少了不必要的递归。
4448
. 使用了记忆化搜索后,一般还可以进行优化,在记忆化搜索的基础上,变成 *自底而上* 的动态规划。
4549

46-
image::images/0322-03.png[{image_attr}]
47-
48-
49-
=== 暴力穷举
50-
51-
[{java_src_attr}]
52-
----
53-
include::{sourcedir}/_0322_CoinChange_2.java[tag=answer]
54-
----
55-
56-
=== 动态规划
50+
image::images/0322-01.png[{image_attr}]
5751

5852
image::images/0322-02.jpeg[{image_attr}]
5953

@@ -66,21 +60,61 @@ F(3) =min(F(3−c~1~),F(3−c ~2~),F(3−c~3~))+1
6660
=1
6761
----
6862

63+
image::images/0322-03.png[{image_attr}]
64+
65+
[[src-0322]]
66+
[tabs]
67+
====
68+
一刷::
69+
+
70+
--
6971
[{java_src_attr}]
7072
----
71-
include::{sourcedir}/_0322_CoinChange_22.java[tag=answer]
73+
include::{sourcedir}/_0322_CoinChange.java[tag=answer]
7274
----
75+
--
7376
74-
[[src-0322]]
77+
二刷(暴力穷举)::
78+
+
79+
--
7580
[{java_src_attr}]
7681
----
77-
include::{sourcedir}/_0322_CoinChange.java[tag=answer]
82+
include::{sourcedir}/_0322_CoinChange_2.java[tag=answer]
7883
----
84+
--
7985
86+
二刷(动态规划(莫名其妙解法))::
87+
+
88+
--
8089
[{java_src_attr}]
8190
----
8291
include::{sourcedir}/_0322_CoinChange_21.java[tag=answer]
8392
----
93+
--
94+
95+
二刷(动态规划)::
96+
+
97+
--
98+
[{java_src_attr}]
99+
----
100+
include::{sourcedir}/_0322_CoinChange_22.java[tag=answer]
101+
----
102+
--
103+
104+
三刷(动态规划)::
105+
+
106+
--
107+
[{java_src_attr}]
108+
----
109+
include::{sourcedir}/_0322_CoinChange_3.java[tag=answer]
110+
----
111+
--
112+
====
113+
114+
== 思考题
115+
116+
. 如何把动态规划代码写得更简洁一些?
117+
. 如何使用自底向上方法来实现一遍动态规划?
84118

85119
== 参考资料
86120

Diff for: logbook/202503.adoc

+5
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,11 @@ endif::[]
200200
|{doc_base_url}/0474-ones-and-zeroes.adoc[题解]
201201
|❌ 动态规划,多维度“物品”就无从下手,还要多练。
202202

203+
|{counter:codes2503}
204+
|{leetcode_base_url}/coin-change/[322. 零钱兑换]
205+
|{doc_base_url}/0322-coin-change.adoc[题解]
206+
|⭕️ 动态规划,完全背包问题
207+
203208
|===
204209

205210
截止目前,本轮练习一共完成 {codes2503} 道题。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.diguage.algo.leetcode;
2+
3+
import java.util.Arrays;
4+
5+
public class _0322_CoinChange_3 {
6+
// tag::answer[]
7+
/**
8+
* @author D瓜哥 · https://www.diguage.com
9+
* @since 2025-04-09 11:36:55
10+
*/
11+
public int coinChange(int[] coins, int amount) {
12+
int max = amount + 1;
13+
// 1、确定 dp 数组(dp table)以及下标的含义
14+
// 兑换 i 元钱,最少需要多少个货币
15+
int[] dp = new int[max];
16+
// 3、dp 数组如何初始化
17+
// 由于求最小值,则初始化比金额大一的数
18+
// 注:初始化为 Integer.MAX_VALUE 时,需要处理溢出问题
19+
Arrays.fill(dp, max);
20+
dp[0] = 0;
21+
// 4、确定遍历顺序,先金额从小到大开始遍历,再逐个货币进行尝试
22+
// 注:这样遍历,就可以尽可能多次使用大大额货币,比如
23+
// f(11) = f(11-5) + 1 = (f(11-5-5) + 1) + 1 = 3
24+
for (int i = 1; i <= amount; i++) {
25+
for (int coin : coins) {
26+
// 2、确定递推公式:f(n+1) = min{f(n+1), f(n) + 1}
27+
if (coin <= i) {
28+
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
29+
}
30+
}
31+
}
32+
return dp[amount] >= max ? -1 : dp[amount];
33+
// 原始解法,当做一个对比放在这里吧
34+
// if (amount == 0) {
35+
// return -1;
36+
// }
37+
// Arrays.sort(coins);
38+
// int[] dp = new int[amount + 1];
39+
// Arrays.fill(dp, Integer.MAX_VALUE);
40+
// dp[0] = 0;
41+
// for (int coin : coins) {
42+
// for (int i = 1; i <= amount; i++) {
43+
// for (int j = 1; j * coin <= i; j++) {
44+
// int pre = dp[i - j * coin];
45+
// if (pre < Integer.MAX_VALUE) {
46+
// dp[i] = Math.min(dp[i], pre + j);
47+
// }
48+
// }
49+
// }
50+
// }
51+
// return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
52+
}
53+
54+
// end::answer[]
55+
public static void main(String[] args) {
56+
new _0322_CoinChange_3().coinChange(new int[]{2}, 3);
57+
}
58+
}

0 commit comments

Comments
 (0)