집은 빨강, 초록, 파랑 중 하나의 색으로 칠해야 한다. 각각의 집을 빨강, 초록, 파랑으로 칠하는 비용이 주어졌을 때, 아래 규칙을 만족하면서 모든 집을 칠하는 비용의 최솟값을 구해보자.
26 40 83
49 60 57
13 89 99
96
이 문제에서 제시하는 조건은 대략 2개로 정리할 수 있다.
우선 첫번째 조건은 어떻게 체크할 수 있을까?
입력 받을 때부터 빨강, 초록, 파랑별로 행을 구분해 두었기 때문에 내가 다음에 검사할 집의 행 인덱스 숫자가 내 집과 다르면 된다.
그렇다면 두번째 조건에서 최소 비용인 것은 어떻게 파악할 수 있을까?
내 집의 다음 집과 행 인덱스 숫자가 다르면서 값이 최소인 것을 선택하는 방식을 처음으로 떠올릴 수 있겠지만 이는 적절하지 않다. 내가 하는 선택이 전체적인 측면에서 최대 이득이라는 보장이 없기에 그럴 것이다. 따라서 최소 비용을 어떻게 효율적으로 찾냐가 관건인 문제라고 볼 수 있다.
import sys
sys.setrecursionlimit(10**6)
answers = []
def dfs(r,c,total):
global answers,colors,N
total += colors[r][c]
if r == N-1:
answers.append(total)
return
else:
for i in range(3):
if i!= c:
dfs(r+1,i,total)
N = int(sys.stdin.readline())
colors = [list(map(int,sys.stdin.readline().split())) for _ in range(N)]
for i in range(3):
dfs(0,i,0)
print(min(answers))
통과
시간복잡도는 O(N)으로 첫번째 풀이보다 훨씬 효율적이다.
이 문제는 완전 이진 트리로도 해석할 수 있다. 아래 코드에서는 첫번째 집을 칠하는 값이 말단 노드인 완전 이진 트리를 상정하고 풀이했다. 예제 입력 1의 경우, 마지막 행의 첫번째 열인 값을 루트 노드로 가진다면 아래 그림의 트리를 탐색하는 것과 같다. 결국 이 문제는 완전 이진 트리의 최소합을 구하는 문제이기에 부분적으로 반복되며 최적 부분 구조를 만족한다. 따라서 DP로 풀이하는 것이 적절할 것이다.

import sys
sys.setrecursionlimit(10**6)
N = int(sys.stdin.readline())
colors = [list(map(int,sys.stdin.readline().split())) for _ in range(N)]
dp = [[0,0,0] for _ in range(N)]
# 말단 노드 값 == 말단 노드의 최소합
dp[0][0], dp[0][1], dp[0][2] = colors[0][0],colors[0][1],colors[0][2]
for i in range(1,N):
dp[i][0] = colors[i][0] + min(dp[i-1][1],dp[i-1][2])
dp[i][1] = colors[i][1] + min(dp[i-1][0],dp[i-1][2])
dp[i][2] = colors[i][2] + min(dp[i-1][0],dp[i-1][1])
print(min(dp[N-1]))