코딩테스트 문제 풀이 (C++)

백준 17404 - RGB거리 2 (C++)

오의현 2024. 6. 11. 15:55

 

여러 개의 집이 순서대로 놓여 있고, 이 집의 외벽에 색을 칠하고자 한다.

하나의 집에 인접한 두 집과 같은 색이 아니도록 칠하고자 한다.

 

각 집마다 색을 칠할 때의 비용이 상이하게 책정되어 있다.

인접한 집과 다른 색상이라는 조건을 유지하며 색을 칠할 때 그 비용의 최소를 구하자.

 

문제 풀이

이 문제는 모든 경우를 탐색하며 그 비용을 구해야 한다.

하지만, 일반적인 완전탐색 방식으로 모든 경우를 탐색하면 말도 안되는 경우의 수가 나온다.

그러므로 다이나믹 프로그래밍을 사용하여 효율적으로 비용의 최소 값을 구해야 한다.

 

먼저, 첫 번째 집의 색상을 하나로 고정하여 시작하는 DP를 3번 반복하는 것이다. 

 

왜 3번을 할까? 첫 번째 집과 마지막 집의 색이 같으면 안된다는 조건 때문이다.

그렇기 때문에 첫 번째 집을 Red로 칠하는 경우, Green으로 칠하는 경우, Blue로 칠하는 경우를 하나씩 확인해야 한다.

 

그럼, 이제 DP를 해보자.

먼저, DP는 2차원 배열로 만들 것이다.

DP[i][j] 가 있다면, i는 집의 순번이고 j는 색상이다. (0은 R, 1은 G, 2는 B)

저장될 값은 최소 비용이다.

 

DP[3][2] 라고 한다면, 3번째 집을 Blue로 칠할 때의 최소 비용이다.

DP[10][1]은 10번째 집을 Green으로 칠할 때의 최소 비용이다.

이렇게 집의 위치와 색상을 함께 고려할 것이다.

 

이 때, DP[i][0] 은 어떻게 구해야 할까?

i번째 집을 빨간색으로 칠한다고 하면, 일단 i-1번째 집은 빨간색이어선 안된다.

또한, i - 1 개의 집을 칠하는데 들어간 총 비용 + i번째 집을 빨간색으로 칠하는 비용이다.

i - 1 개의 집을 칠하는데 들어가는 총 비용은 i - 1 번째 집이 파란색이냐, 초록색이냐에 따라 다를 수 있다.

 

그러므로, DP[i][0] 은 std::min(DP[i - 1][1] + Cost[i][j], DP[i - 1][2] + Cost[i][j]) 가 될 것이다.

DP[i][0]에 저장할 값은 최소비용이므로, 이전의 집이 파란색일 때의 총 비용과 초록색일 때의 총 비용중 최소 값을 기준으로 현재 집을 칠하는 비용을 더해서 총 비용의 최소값을 갱신하는 것이다.

 

이렇게 마지막 집까지 모두 갱신했다고 해보자.

그러면, 마지막 집이 R인 경우의 총 비용, G인 경우의 총 비용, B인 경우의 총 비용 이렇게 3개의 값을 구할 수 있다.

이 3개의 비용 중 가장 작은 비용이 첫 번째 집을 R로 칠하는 경우 중 최소 비용인 것이다.

 

하지만, 마지막 집은 첫 번째 집과 같은 색일 수 없다는 조건이 있으므로, 마지막 집이 R인 경우의 총 비용은 제외하고, 마지막 집이 G인 경우의 총 비용과 B인 경우의 총 비용만 고려하여 최소를 구하면 된다.

 

첫 번째 집을 R로 칠할 때의 최소 비용, G로 칠할 때의 최소 비용, B로 칠할 때의 최소비용 총 3개를 구한 뒤 셋 중 최소비용을 구하면 그 값이 답이 된다.

 

풀이 코드

#define BIGNUMBER 999999999

먼저, 최소값을 갱신하기 전 초기값을 잡기 위해 충분히 큰 값을 하나 정의해주었다.

int NumOfHouse = 0;
std::cin >> NumOfHouse;

std::vector<std::vector<int>> Cost(NumOfHouse, std::vector<int>(3, 0));
for (int i = 0; i < NumOfHouse; i++)
{
    for (int j = 0; j < 3; j++)
    {
        std::cin >> Cost[i][j];
    }
}

이후 입력을 받아주었다. 모든 비용을 Cost에 저장해주었다. 

Cost[i][j]는 i번째 집을 j색(0 = R, 1 = G, 2 = B)으로 칠하는 비용이다.

int Answer = BIGNUMBER;

std::vector<std::vector<int>> DP(NumOfHouse, std::vector<int>(3, BIGNUMBER));
for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < DP.size(); j++)
    {
        std::fill(DP[j].begin(), DP[j].end(), BIGNUMBER);
    }

    DP[0][0] = Cost[0][0];
    DP[0][1] = Cost[0][1];
    DP[0][2] = Cost[0][2];

    for (int j = 0; j < 3; j++)
    {
        if (i == j)
        {
            continue;
        }

        DP[1][j] = DP[0][i] + Cost[1][j];
    }

    for (int j = 2; j < NumOfHouse; j++)
    {
        DP[j][0] = std::min(DP[j - 1][1] + Cost[j][0], DP[j - 1][2] + Cost[j][0]);
        DP[j][1] = std::min(DP[j - 1][0] + Cost[j][1], DP[j - 1][2] + Cost[j][1]);
        DP[j][2] = std::min(DP[j - 1][0] + Cost[j][2], DP[j - 1][1] + Cost[j][2]);
    }

    for (int j = 0; j < 3; j++)
    {
        if (j == i)
        {
            continue;
        }

        Answer = std::min(Answer, DP[NumOfHouse - 1][j]);
    }
}

먼저,std::fill을 사용하여 DP배열을 초기화해주었다.

하나의 배열을 재활용 할 것이기 때문에 초기화 코드를 넣어주었다.

 

DP는 위에서 말했듯이, 첫번째 집이 R인 경우, G인 경우, B인 경우 총 3개의 경우를 진행할 것이다.

 

DP[0][0], DP[0][1], DP[0][2] 는 Cost[0][0], Cost[0][1], Cost[0][2] 로 초기화 해 주었다.

 

이후 반복문으로 DP[1]도 갱신해주었다. 

그리고 이후, DP[2]부터 DP[NumOfHouse]까지 모두 갱신해주었다.

 

DP[1]만 따로 갱신한 이유는 첫번째 집을 칠하는 색을 하나로 정해두었기 때문에, DP[1]은 첫번째 집이 어떤 색인지 고려하여 값을 계산할 필요가 없기 때문이다. 첫번째 집이 빨간색이라면 그에 맞게 DP[1]을 갱신하였고, 파란색이라면 그에 맞게 갱신한 것이다.

 

DP배열을 모두 갱신한 뒤에, DP배열의 마지막에 저장된 총 비용 중 최소값을 Answer에 저장해주었다.

 

이를, 총 3번(첫 번째 집이 R인경우, G인 경우, B인 경우) 실행하였고, 최종적으로 Answer에 저장된 값이 정답이 된다.

 

std::cout << Answer;

return 0;

마지막으로 답을 출력해주면 된다.

 

 

코드 전문

더보기
#include <iostream>
#include <vector>
#include <algorithm>

void Init()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
}

#define BIGNUMBER 999999999

int main()
{
    Init();

    int NumOfHouse = 0;
    std::cin >> NumOfHouse;

    std::vector<std::vector<int>> Cost(NumOfHouse, std::vector<int>(3, 0));
    for (int i = 0; i < NumOfHouse; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cin >> Cost[i][j];
        }
    }

    int Answer = BIGNUMBER;

    std::vector<std::vector<int>> DP(NumOfHouse, std::vector<int>(3, BIGNUMBER));
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < DP.size(); j++)
        {
            std::fill(DP[j].begin(), DP[j].end(), BIGNUMBER);
        }

        DP[0][0] = Cost[0][0];
        DP[0][1] = Cost[0][1];
        DP[0][2] = Cost[0][2];

        for (int j = 0; j < 3; j++)
        {
            if (i == j)
            {
                continue;
            }

            DP[1][j] = DP[0][i] + Cost[1][j];
        }

        for (int j = 2; j < NumOfHouse; j++)
        {
            DP[j][0] = std::min(DP[j - 1][1] + Cost[j][0], DP[j - 1][2] + Cost[j][0]);
            DP[j][1] = std::min(DP[j - 1][0] + Cost[j][1], DP[j - 1][2] + Cost[j][1]);
            DP[j][2] = std::min(DP[j - 1][0] + Cost[j][2], DP[j - 1][1] + Cost[j][2]);
        }

        for (int j = 0; j < 3; j++)
        {
            if (j == i)
            {
                continue;
            }

            Answer = std::min(Answer, DP[NumOfHouse - 1][j]);
        }
    }

    std::cout << Answer;

    return 0;
}