그리디

거스름돈

큰 동전부터 주면 되나

동전으로 거스름돈을 만들어 보세요. 줄 수 있는 동전 중 가장 큰 동전부터 한 개씩 줍니다.

1260원을 거슬러 줍니다. 가장 적은 개수로 주는 것이 목표예요. 줄 수 있는 동전 중 가장 큰 동전부터 눌러 한 개씩 줍니다.

남은 돈 1260원

준 동전 0개

  1. 아직 없음
지금까지 쓴 동전 0개

세트 A는 큰 동전부터 줘서 0개로 끝났습니다. 세트 B는 큰 동전부터 주면 0개인데, 다른 방법으로는 0개입니다.

4 + 1 + 1세트 B 를 큰 동전부터 주면 이렇게 됩니다 (3개).

3 + 3하지만 이렇게 주면 2개입니다.

세트 B · 큰 동전부터3개
세트 B · 가장 적게2개
같은 6원입니다. 세트 A 는 큰 동전부터 줘도 6개로 가장 적었습니다.

지금 가장 좋아 보이는 것(가장 큰 동전)을 고르고 되돌리지 않는 방법 = 그리디(greedy)

세트 A 에서는 맞았고 세트 B 에서는 틀렸습니다. 그리디는 늘 맞는 방법이 아닙니다. 모든 경우를 따져 맞게 만드는 방법은 작은 경우부터 표를 채우는 DP 입니다. 앞 장의 0/1 배낭 이 그 예입니다.

큰 동전이 작은 동전의 배수일 때는 맞다

  • 세트 A — 500 = 5 × 100, 100 = 2 × 50, 50 = 5 × 10. 큰 동전 하나는 작은 동전 여러 개와 같아서 큰 동전을 줘도 손해가 없다
  • 세트 B — 4 는 3 의 배수가 아니다. 4원을 먼저 주는 바람에 3 + 3 이 될 자리를 놓쳤다
  • 그래서 — 동전이 서로 배수면 큰 것부터 줘도 된다. 배수가 아니면 틀릴 수 있다

코드는 큰 동전부터 빼 나가는 것이 전부다

다음을 눌러 한 줄씩 넘기며 남은 돈이 줄고 바구니가 차는 걸 보세요. 처음 화면은 ①에서 마지막으로 한 세트 B 입니다.

남은 돈 6원

  1. 4원
  2. 3원
  3. 1원

바구니 0개

  1. 아직 없음

다음을 눌러 한 줄씩 넘겨 보세요

amount = 6

greedy_coins.py
  1. 1def greedy_coins(coins, amount):
  2. 2 coins = sorted(coins, reverse=True)
  3. 3 given = []
  4. 4 for coin in coins:
  5. 5 while amount >= coin:
  6. 6 amount -= coin
  7. 7 given.append(coin)
  8. 8 return given
0 / 15

같은 입력을 모든 경우로 따지면 2개(3 + 3)입니다. 큰 동전부터 준 3개(4 + 1 + 1)보다 적습니다.

  • sorted(coins, reverse=True) — 동전을 큰 것부터 늘어놓는다
  • while amount >= coin — 남은 돈에 이 동전을 낼 수 있는 동안 계속 낸다
  • amount -= coin — 낼 때마다 남은 돈이 줄어든다. 한 번 낸 동전은 물리지 않는다

동전이 1원·5원·12원일 때, 15원을 큰 동전부터 주면 몇 개일까요?

그리디에서 틀리는 자리

  • 정렬을 빼면 — [1, 3, 4] 순서 그대로 6원을 주면 1원부터 줘서 6개가 된다. 큰 동전부터 줄 때는 3개다
  • 배수가 아닌 세트에서는 — [1, 3, 4] 6원은 큰 동전부터 3개, 가장 적게는 2개다
  • 거슬러 줄 수 없는 돈은 — [5, 10] 으로 12원을 주면 에러 없이 [10] 이 나오고 남은 2원은 말없이 사라진다. 남은 돈이 0 인지 따로 확인해야 한다
  • while 대신 // 와 % — 세트 A 로 10억 원이면 while 이 200만 번 돌아 0.04초 걸린다. 몫을 amount // coin 으로 한 번에 구하면 4번 계산해 0.000003초다(Python 3.14, 컴퓨터마다 다르다)

문제에서 이 말이 보이면

최소 개수큰 것부터 고르면 된다(배수 관계)증명할 수 있으면 그리디, 아니면 DP

다음 장에서는 끝나는 시간이 빠른 회의부터 고르는 회의실 배정을 봅니다.