문제
Input : 정수배열 coins, 정수 amount
Output : coins 원소의 합으로, amount를 만들 수 있는 최소한의 동전 개수 반환, 만들 수 없다면 -1 반환
※ 각 원소는 여러 번 제한 없이 사용 가능하다.
시간 복잡도
※1초에 1억번을 대략적인 기준으로 삼는다.
사실 보자마자 n을 어떤 걸로 해야 할지 감이 안 잡혔다.
그래서 계획 수립 후 n을 구했다.
해결 계획
먼저 0부터 amount까지 최소값을 저장해가면서 진행할까 고민을하다가,
amount가 최대 10,000인걸 확인 후에 메모제이션을 사용하기로 확정지었다.
아래의 코드와 같은 느낌으로, 하나의 인덱스에서 모든 코인 값을 더해 점차 채워가는 방식으로 하면 될 것 같았다.
FOR each coin in coins
dp[i] = dp[i - coin[0]];
이렇게 식이 어느정도 나온 후에 복잡도를 구할 수 있었다.
$O(n) = amount * coins.size = 10'000 \times 12 = 120'000$
이정도면 지금 진행하려는 방법은 충분히 통과가 가능하다.
그 후에 어떻게 채워갈지 좀 더 자세하게 생각했다.
그 중에 먼저 채워넣은 값이 최소값이 유지가 가능한가? 이게 핵심이였는데,
나는 몇 번 해보고 유지가 되네? 하고 넘어갔지만, 해당 케이스가 존재함을 풀이 포기 후에야 알았다.
그 후에 수도코드를 작성했다.
//초기값 설정
Fill dp with 0
FOR each element in coins
dp[element] = 1
//메모제이션
FOR (i <- coins[0] TO amount)
{
// 만들 수 없으면 넘어간다, 만들 수 없으면 0으로 계산하고 마지막에 -1로 변경한다.
IF (dp[i] == 0) CONTINUE
// 만들 수 있으면 다음 것들을 추가함
FOR each element in coins
{
// 값이 없으면 새로 삽입
IF (dp[i + element] == 0)
dp[i + element] = dp[i] + 1
// 이미 값이 있으면 최소값 구하기
ELSE
dp[i + element] = min(dp[i + element], dp[i] + 1)
}
}
IF (dp[amount] == 0) return -1
return dp[amount]
이 후에 몇번 시뮬레이션을 돌려보고 구현에 들어갔다.
구현(오답)
int coinChange(vector<int>& coins, int amount) {
if (amount == 0 )return 0;
// 초기값 설정
int dp[10001] = {0};
for (auto& data : coins)
{
dp[data] = 1;
}
// 메모제이션
for (int i = coins[0]; i < amount; ++i)
{
// 만들 수 없으면 넘어간다, 만들 수 없으면 0으로 계산하고 마지막에 -1로 변경한다.
if (dp[i] == 0) continue;
// 만들 수 있으면 다음 것들을 추가함
for (int data : coins)
{
// 기존에 값이 들어있으면 넘어간다.
if (0 < dp[i + data]) continue;
dp[i + data] = dp[i] + 1;
}
}
// 출력
if (dp[amount] == 0) return -1;
return dp[amount];
}
되돌아보기
핵심 식은 잘 파악한 거 같은데, 디테일을 잘 못 살렸던 것 같다.
먼저 채워진 값이 최솟값이다
성립하지 않았다, 왜냐하면 정렬이 되었다는 가정은 없기 때문이다.
사실 정렬문제는 어느 정도 생각했는데, 정렬이 되어 있지 않을까?라는 먼가 이때는 왜 이렇게 생각했지?
암튼 안일해서 틀렸다.
Out of bound
$coins[i] \le 2^{31}-1$에 대해 더 고려하지 못했다.
for (int data : coins)
{
// 기존에 값이 들어있으면 넘어간다.
if (0 < dp[i + data]) continue;
dp[i + data] = dp[i] + 1;
}
이 부분에서 data[$2^{31} + \alpha$]가 나올 수 있게 됨으로 Out of bound가 발생하게 되었다.
그래서 아래처럼 amount 이상의 값은 넘어가는 식으로 해결했어야 했다.
for (int data : coins)
{
if (amount < data) continue;
...
}
하지만 더 좋은 방법이 있었다.
Backward DP
지금처럼 dp[i + ?]로 미래의 값을 채워넣는게 아닌,
dp[i - ?]식으로 과거의 값을 참조하여 DP를 채워 넣으면 Out of bound가 발생할 확률이 매우 낮아진다!
해당 방법은 다음에 적용해 봐야겠다.
개선 코드
나중에 다시 풀어보고 작성한다.
'코딩 테스트 > 알고리즘 풀이' 카테고리의 다른 글
| [Leet code - 64] Minimum Path Sum ✅ (0) | 2026.03.06 |
|---|---|
| [Leet code - 121] Best Time to Buy and Sell Stock ✅ (0) | 2026.03.05 |
| [Leet code - 62] Unique Paths ❌ (0) | 2026.03.04 |
| [Leet code - 198] House Robber ✅ (0) | 2026.03.03 |
| [Leet code - 973] K Closest Points to Origin ✅ (0) | 2026.02.27 |
| [Leet code - 739] Daily Temperatures ✅ (0) | 2026.02.26 |
| [Leet code - 20] Valid Parentheses ✅ (0) | 2026.02.26 |
| [Leet code - 75] Sort Colors ✅ (0) | 2026.02.13 |
