BOJ 17404 RGB거리 2 (Java)

사람·2025년 2월 6일
1

BOJ

목록 보기
29/75

문제

https://www.acmicpc.net/problem/17404

RGB거리에는 집이 N개 있다. 거리는 선분으로 나타낼 수 있고, 1번 집부터 N번 집이 순서대로 있다.

집은 빨강, 초록, 파랑 중 하나의 색으로 칠해야 한다. 각각의 집을 빨강, 초록, 파랑으로 칠하는 비용이 주어졌을 때, 아래 규칙을 만족하면서 모든 집을 칠하는 비용의 최솟값을 구해보자.

1번 집의 색은 2번, N번 집의 색과 같지 않아야 한다.
N번 집의 색은 N-1번, 1번 집의 색과 같지 않아야 한다.
i(2 ≤ i ≤ N-1)번 집의 색은 i-1, i+1번 집의 색과 같지 않아야 한다.

입력
첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나 같은 자연수이다.

출력
첫째 줄에 모든 집을 칠하는 비용의 최솟값을 출력한다.

예제 입력 1
3
26 40 83
49 60 57
13 89 99
예제 출력 1
110

예제 입력 2
3
1 100 100
100 1 100
100 100 1
예제 출력 2
3

예제 입력 3
3
1 100 100
100 100 100
1 100 100
예제 출력 3
201

예제 입력 4
6
30 19 5
64 77 64
15 19 97
4 71 57
90 86 84
93 32 91
예제 출력 4
208

예제 입력 5
8
71 39 44
32 83 55
51 37 63
89 29 100
83 58 11
65 13 15
47 25 29
60 66 19
예제 출력 5
253

접근

RGB거리 1이 되게 쉬웠고... 이 문제도 대충 보니 RGB거리 1이랑 큰 차이가 없어 보여서 만만하게 보고 덤볐는데 생각보다 어려워서 많이 헤맸다. 허허....
1번 집의 색과 N번 집의 색이 달라야 한다는 걸 어떻게 처리해야 할지가 답이 안 나와서 너무 답답했다... 처음에는 N번 집에 대해서까지 최소 비용을 구하고 다시 1번 집을 선택해야 되나? 생각했는데 이미 처음에 1번 집을 선택했는데 또 선택한다는 게 말이 될 리가 없다. 지금 생각해보면 dp를 딱 한 번만 수행하고 끝내야 한다는 생각에 너무 갇혀 있었던 것 같다. 지금까지 그런 문제만 접해 왔으니까... 푼 문제가 많아질수록 실력이 쌓이는 건 맞는 거 같은데 사고가 내가 풀었던 문제들 안에만 갇혀 있는 건 아닌가 하는 생각이 들었다. 사고를 좀 더 유연하게 가져가야 할 것 같다.

무튼 간에, 이 문제는 한 번의 탑다운이나 버텀업으로는 해결할 수가 없었다.
1. 첫 번째 집의 색깔을 빨간색으로 정해두고, 마지막 집을 초록색 또는 파란색 중 하나로 색칠
2. 첫 번째 집의 색깔을 초록색으로 정해두고, 마지막 집을 빨간색 또는 파란색 중 하나로 색칠
3. 첫 번째 집의 색깔을 파란색으로 정해두고, 마지막 집을 빨간색 또는 초록색 중 하나로 색칠

위 세 가지 경우에 대해 각각 최소 비용을 구한 후, 그 중에서 최솟값을 찾아야 하는 거였다.

구현

import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        int[][] dp = new int[N + 1][3];
        int[][] cost = new int[N + 1][3];
        int firstR;
        int firstG;
        int firstB;
        for (int i = 1; i <= N; i++) {
            String[] input = br.readLine().split(" ");
            cost[i][0] = Integer.parseInt(input[0]);
            cost[i][1] = Integer.parseInt(input[1]);
            cost[i][2] = Integer.parseInt(input[2]);
        }

        if (N == 2) {
            dp[1][0] = cost[1][0];
            dp[1][1] = cost[1][1];
            dp[1][2] = cost[1][2];
            dp[2][0] = Math.min(dp[1][1], dp[1][2]) + cost[2][0];
            dp[2][1] = Math.min(dp[1][0], dp[1][2]) + cost[2][1];
            dp[2][2] = Math.min(dp[1][0], dp[1][1]) + cost[2][2];
            System.out.println(Math.min(dp[2][0], Math.min(dp[2][1], dp[2][2])));
            return;
        }
		
        // 1번 집을 빨간색으로 칠하는 경우
        dp[2][0] = 2001; // 2번 집은 빨간색으로 칠할 수 없음
        dp[2][1] = cost[1][0] + cost[2][1]; // 1번 집 빨강, 2번 집 초록
        dp[2][2] = cost[1][0] + cost[2][2]; // 1번 집 빨강, 2번 집 파랑
        for (int i = 3; i <= N; i++) {
            dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + cost[i][0];
            dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + cost[i][1];
            dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + cost[i][2];
        }
        firstR = Math.min(dp[N][1], dp[N][2]);

		// 1번 집을 초록색으로 칠하는 경우
        dp[2][0] = cost[1][1] + cost[2][0]; // 1번 집 초록, 2번 집 빨강
        dp[2][1] = 2001; // 2번 집은 초록색으로 칠할 수 없음
        dp[2][2] = cost[1][1] + cost[2][2]; // 1번 집 초록, 2번 집 파랑
        for (int i = 3; i <= N; i++) {
            dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + cost[i][0];
            dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + cost[i][1];
            dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + cost[i][2];
        }
        firstG = Math.min(dp[N][0], dp[N][2]);

		// 1번 집을 파란색으로 칠하는 경우
        dp[2][0] = cost[1][2] + cost[2][0]; // 1번 집 파랑, 2번 집 빨강
        dp[2][1] = cost[1][2] + cost[2][1]; // 1번 집 파랑, 2번 집 초록
        dp[2][2] = 2001; // 2번 집은 파란색으로 칠할 수 없음
        for (int i = 3; i <= N; i++) {
            dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + cost[i][0];
            dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + cost[i][1];
            dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + cost[i][2];
        }
        firstB = Math.min(dp[N][0], dp[N][1]);

        System.out.println(Math.min(firstR, Math.min(firstG, firstB)));
    }
}

코드에 불필요한 중복이 많은데 반복문을 사용하면 더 짧게 쓸 수 있을 것 같지만... 지금이 더 직관적이긴 한 것 같아서 그냥 냅두려고 한다...

profile
알고리즘 블로그 아닙니다.

0개의 댓글