1
1
---
2
2
layout : post
3
- title : (Leetcode) 322 - Coin Change
3
+ title : (Leetcode) 322 - Coin Change 풀이
4
4
categories : [스터디-알고리즘]
5
- tags : [파이썬, 알고리즘, python, algorithm, Leetcode, DP, coin]
5
+ tags : [파이썬, 알고리즘, python, algorithm, Leetcode, DP, coin, java, stream ]
6
6
date : 2024-02-26 22:00:00 +0900
7
7
image :
8
8
path : /assets/images/2024-02-26-leetcode-322/page1.png
@@ -22,7 +22,7 @@ DP 문제이다.
22
22
23
23
### 처음 생각
24
24
25
- 처음에는 큰 동전부터 넣어서 될 때까지 하면 베스트 일 것이라고 생각했다. 하지만 그렇게 간단하지는 않았다.
25
+ 처음에는 큰 동전부터 넣어서 될 때까지 하면 베스트 일 것이라고 생각했다. 하지만 그렇게 간단하지는 않았다.
26
26
27
27
예를들어 ` [100, 30, 1] ` 이라는 동전이 있다고 해보자.
28
28
이때 주어지는 amount는 120 이라고 하면
@@ -31,7 +31,7 @@ DP 문제이다.
31
31
32
32
### 중간 생각
33
33
34
- 그래서 모든 경우의 수를 다 찾아봐야 겠다고 생각했다.
34
+ 그래서 모든 경우의 수를 다 찾아봐야 겠다고 생각했다.
35
35
처음에는 잘 먹히는 듯 보였으나, 역시나 복잡한 경우로 인해 타임아웃이 발생되었다.
36
36
37
37
``` python
@@ -129,17 +129,137 @@ print(solution.coinChange([1, 2, 5], 10))
129
129
![ example page5] ( /assets/images/2024-02-26-leetcode-322/page5.png )
130
130
131
131
### 시간복잡도
132
+
132
133
- 이중 반복문 사용
133
134
- 바깥쪽 반복문 : amount 만큼 반복 (O(n))
134
135
- 안쪽 반복문 : coins 리스트의 length 만큼 반복 (O(m))
135
136
136
137
` O(n * m) ` 의 시간 복잡도를 가짐
137
138
138
139
### 공간복잡도
140
+
139
141
amount 만큼 dp array를 생성함
140
142
141
143
` O(n) ` 의 공간 복잡도를 가짐
142
144
143
145
## 결론
144
146
145
- DP 문제를 더 많이 접해봐야겠다는 생각이 들었다.
147
+ DP 문제를 더 많이 접해봐야겠다는 생각이 들었다.
148
+
149
+ ## java 로 다시 풀기 (24.07.09)
150
+
151
+ 먼저는 stream으로 풀었다. 이미 들린 dp pointer 일 때, 기존에 등록된 값보다 클 경우 (` dp[currentPointer] > dp[amount] + 1 ` ) 이후 dfs 단계를 생략하도록 한 것이 포인트이다.
152
+
153
+ 처음으로 ` dp[0] ` 에 도달했다고 해서 최소값이 아니기 때문에 전체 케이스를 고려해야 하는데 그렇다고 진짜로 전체 케이스를 확인해본다면 timeout이 발생된다. 따라서 이 조건문을 찾아내지 못하면 timeout이 발생된다.
154
+
155
+ ``` java
156
+ class Solution {
157
+ public int coinChange (int [] coins , int amount ) {
158
+ if (amount == 0 ) {
159
+ return 0 ;
160
+ }
161
+
162
+ int [] dp = new int [amount + 1 ];
163
+
164
+ List<Integer > sortedCoins = Arrays . stream(coins). boxed()
165
+ .sorted(Collections . reverseOrder())
166
+ .toList();
167
+
168
+ sortedCoins. forEach(coin - > dfs(dp, sortedCoins, amount, coin));
169
+
170
+ return dp[0 ] == 0 ? - 1 : dp[0 ];
171
+ }
172
+
173
+ void dfs (int [] dp , List<Integer > coins , int amount , int selectedCoin ) {
174
+ int currentPointer = amount - selectedCoin;
175
+ if (currentPointer < 0 ) {
176
+ return ;
177
+ }
178
+
179
+ if (dp[currentPointer] == 0 || dp[currentPointer] > dp[amount] + 1 ) {
180
+ dp[currentPointer] = dp[amount] + 1 ;
181
+ coins. forEach(coin - > dfs(dp, coins, currentPointer, coin));
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ stream 의 경우 for loop 보다는 느리지만 이해하기에는 훨씬 좋다. 더 직관적이다.
188
+
189
+ ![ java-using-for-loop] ( /assets/images/2024-02-26-leetcode-322/java-using-for-loop.png )
190
+
191
+ 다만 성능을 더 최적화 하기 위해서 for loop를 이용해서 풀도록 바꾸면 다음과 같다.
192
+
193
+ ``` java
194
+ class Solution {
195
+ public int coinChange (int [] coins , int amount ) {
196
+ if (amount == 0 ) {
197
+ return 0 ;
198
+ }
199
+
200
+ int [] dp = new int [amount + 1 ];
201
+
202
+ Arrays . sort(coins);
203
+ for (int i = coins. length - 1 ; i > - 1 ; i-- ) {
204
+ dfs(dp, coins, amount, coins[i]);
205
+ }
206
+
207
+ return dp[0 ] == 0 ? - 1 : dp[0 ];
208
+ }
209
+
210
+ void dfs (int [] dp , int [] coins , int amount , int selectedCoin ) {
211
+ int currentPointer = amount - selectedCoin;
212
+ if (currentPointer < 0 ) {
213
+ return ;
214
+ }
215
+
216
+ if (dp[currentPointer] == 0 || dp[currentPointer] > dp[amount] + 1 ) {
217
+ dp[currentPointer] = dp[amount] + 1 ;
218
+ for (int i = coins. length - 1 ; i > - 1 ; i-- ) {
219
+ dfs(dp, coins, currentPointer, coins[i]);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ ![ java-using-for-loop] ( /assets/images/2024-02-26-leetcode-322/java-using-for-loop.png )
227
+
228
+ stream을 사용했을 때보다 시간이 단축된 것을 볼 수 있다.
229
+
230
+ ### TS, SC
231
+
232
+ 코인의 수를 n 이라고 했을 때, ` O(n * amount ^ 2) ` 의 시간복잡도와 ` O(amount) ` 의 공간복잡도를 가진다.
233
+
234
+ ## java 모범 답안 (더 효율적인 방법)
235
+
236
+ 위 코드는 dp 라는 array를 사용하긴 했지만, 사실 brute force 에 가까운 방법이다.
237
+
238
+ 아래와 같이 작성하면 더 효율적으로 동작한다.
239
+
240
+ ``` java
241
+ public class Solution {
242
+ public int coinChange (int [] coins , int amount ) {
243
+ int max = amount + 1 ;
244
+ int [] dp = new int [amount + 1 ];
245
+ Arrays . fill(dp, max);
246
+ dp[0 ] = 0 ;
247
+
248
+ for (int i = 1 ; i <= amount; i++ ) {
249
+ for (int j = 0 ; j < coins. length; j++ ) {
250
+ if (coins[j] <= i) {
251
+ dp[i] = Math . min(dp[i], dp[i - coins[j]] + 1 );
252
+ }
253
+ }
254
+ }
255
+
256
+ return dp[amount] > amount ? - 1 : dp[amount];
257
+ }
258
+ }
259
+ ```
260
+
261
+ ### TS, SC
262
+
263
+ 코인의 수를 n 이라고 했을 때, ` O(n * amount) ` 의 시간복잡도와 ` O(amount) ` 의 공간복잡도를 가진다.
264
+
265
+ ![ java-using-dp] ( /assets/images/2024-02-26-leetcode-322/java-using-dp.png )
0 commit comments