(BOJ)-1149, Python
(BOJ)-1149, Python
문제 요약
출처: (BOJ)1149 - RGB거리
집의 수($N$)과 각 집에 칠할 수 있는 Red, Green, Blue 색상의 비용이 주어진다. 모든 집을 칠할 때, 아래 조건을 만족하면서 모든 집을 칠하는데 드는 최소 비용을 구하는 문제이다.
- $i$번 째 집은 $i-1, i+1$의 집과 다른 색을 칠해야 한다.($2\le i \le N-1$)
접근 과정
1. 완전탐색 접근(재귀)
RGB 중 하나를 선택한 경우, 총 세 개의 경우로 나누기 위해 재귀를 활용하여 접근하기로 했다. 결과는 시간초과였다.
코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
N = int(input())
RGB = [list(map(int, input().split())) for _ in range(N)]
min_value = 1e9
def recursion(idx, color, value):
global min_value
if idx == N:
min_value = min(min_value, value)
return
if color == 0: #전 컬러가 RED(0)이라면
recursion(idx+1, 1, value + RGB[idx][1])
recursion(idx+1, 2, value + RGB[idx][2])
elif color == 1: #전 컬러가 GREEN(1)이라면
recursion(idx+1, 0, value + RGB[idx][0])
recursion(idx+1, 2, value + RGB[idx][2])
else: #전 컬러가 BLUE(2)이라면
recursion(idx+1, 0, value + RGB[idx][0])
recursion(idx+1, 1, value + RGB[idx][1])
recursion(0, 0, 0)
recursion(0, 1, 0)
recursion(0, 2, 0)
print(min_value)
2. DP 접근(메모이제이션)
바로 재귀 코드를 바탕으로 DP(메모이제이션) 로 접근하였고, 결과는 맞았습니다!
코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import sys
sys.setrecursionlimit(40000) #파이썬 재귀 한도로 런타임 에러 발생 후 추가함
N = int(input())
RGB = [list(map(int, input().split())) for _ in range(N)]
dp = [[-1 for _ in range(3)] for _ in range(N)]
def recursion(idx, rgb):
if idx == N:
return 0
if dp[idx][rgb] != -1:
return dp[idx][rgb]
if rgb == 0:
dp[idx][rgb] = min(recursion(idx+1, 1) + RGB[idx][1], recursion(idx+1, 2) + RGB[idx][2])
if rgb == 1:
dp[idx][rgb] = min(recursion(idx+1, 0) + RGB[idx][0], recursion(idx+1, 2) + RGB[idx][2])
if rgb == 2:
dp[idx][rgb] = min(recursion(idx+1, 0) + RGB[idx][0], recursion(idx+1, 1) + RGB[idx][1])
return dp[idx][rgb]
for i in range(3):
recursion(0,i)
print(min(dp[0]))
3. DP 접근(점화식)
위에서 문제를 Top-Down으로 접근했다면, 이번에는 Bottom-Up으로 접근해보았다. 복잡한 규칙을 생각하지도 않아도 되고, 현재 집에서 선택한 색상과 다른 색상 중 최소 비용을 선택하며 결과를 업데이트하며 나아가면 된다. 이를 바탕으로 점화식을 다음과 같이 세울 수 있다.
- R을 선택했다면,
- $dp[idx+1][r] = min(dp[idx][g], dp[idx][b]) + cost[idx][r]$
- G를 선택했다면,
- $dp[idx+1][g] = min(dp[idx][r], dp[idx][b]) + cost[idx][g]$
- B를 선택했다면,
- $dp[idx+1][b] = min(dp[idx][r], dp[idx][g]) + cost[idx][b]$
코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
N = int(input())
RGB = [list(map(int, input().split())) for _ in range(N)]
dp = [[0 for _ in range(3)] for _ in range(N+1)]
for idx in range(N):
for rgb in range(3):
if rgb == 0: #RED
dp[idx+1][rgb] = min(dp[idx][1], dp[idx][2]) + RGB[idx][rgb]
if rgb == 1: #GREEN
dp[idx+1][rgb] = min(dp[idx][0], dp[idx][2]) + RGB[idx][rgb]
if rgb == 2: #BLUE
dp[idx+1][rgb] = min(dp[idx][0], dp[idx][1]) + RGB[idx][rgb]
print(min(dp[N]))
회고
어려운 규칙을 알아내야만 풀 수 있는 문제가 아니지만, Bottom-Up 접근을 연습해보기에 좋은 문제라고 생각한다. 코드가 더 간결하게 나오는 것 같다. 아직까지는 경우의 수를 쪼개는 문제에서는 Top-down 접근이 아무래도 조금 더 직관적이고 구현하기 수월한 것 같다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.