버미

백준 25682번 - 체스판 다시 칠하기 2(C++) 본문

코딩 트레이닝

백준 25682번 - 체스판 다시 칠하기 2(C++)

Bum_2 2023. 8. 11. 18:48

체스판 다시 칠하기 2

시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 256 MB 4290 1717 1282 39.592%

문제

지민이는 자신의 저택에서 MN개의 단위 정사각형으로 나누어져 있는 M×N 크기의 보드를 찾았다. 어떤 정사각형은 검은색으로 칠해져 있고, 나머지는 흰색으로 칠해져 있다. 지민이는 이 보드를 잘라서 K×K 크기의 체스판으로 만들려고 한다.

체스판은 검은색과 흰색이 번갈아서 칠해져 있어야 한다. 구체적으로, 각 칸이 검은색과 흰색 중 하나로 색칠되어 있고, 변을 공유하는 두 개의 사각형은 다른 색으로 칠해져 있어야 한다. 따라서 이 정의를 따르면 체스판을 색칠하는 경우는 두 가지뿐이다. 하나는 맨 왼쪽 위 칸이 흰색인 경우, 하나는 검은색인 경우이다.

보드가 체스판처럼 칠해져 있다는 보장이 없어서, 지민이는 K×K 크기의 체스판으로 잘라낸 후에 몇 개의 정사각형을 다시 칠해야겠다고 생각했다. 당연히 K×K 크기는 아무데서나 골라도 된다. 지민이가 다시 칠해야 하는 정사각형의 최소 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 정수 N, M, K가 주어진다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.

출력

첫째 줄에 지민이가 잘라낸 K×K 보드를 체스판으로 만들기 위해 다시 칠해야 하는 정사각형 개수의 최솟값을 출력한다.

제한

  • 1 ≤ N, M ≤ 2000
  • 1 ≤ K ≤ min(N, M)

예제 입력 1 

4 4 3
BBBB
BBBB
BBBW
BBWB

예제 출력 1 

2

예제 입력 2 

8 8 8
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBBBWBW
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBWBWBW

예제 출력 2 

1

예제 입력 3 

10 13 10
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
WWWWWWWWWWBWB
WWWWWWWWWWBWB

예제 출력 3 

30

예제 입력 4 

9 23 9
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBWWWWWWWW

예제 출력 4 

40

문제 풀이

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int n, m, k;
vector<vector<int>> prefix_sum;
vector<vector<char>> board;

int chess(char color) {
    prefix_sum.assign(n + 1, vector<int>(m + 1, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            int value = ((i + j) % 2 == 0) ? (board[i][j] == color) : (board[i][j] != color);
            prefix_sum[i + 1][j + 1] = prefix_sum[i][j + 1] + prefix_sum[i + 1][j] - prefix_sum[i][j] + value;
        }
    }

    int count = 99999999;
    for (int i = 0; i <= n - k; i++) {
        for (int j = 0; j <= m - k; j++) {
            count = min(count, prefix_sum[i + k][j + k] - prefix_sum[i + k][j] - prefix_sum[i][j + k] + prefix_sum[i][j]);
        }
    }
    return count;
}

int main() {
    cin >> n >> m >> k;
    board.assign(n, vector<char>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> board[i][j];
        }
    }

    cout << min(chess('B'), chess('W')) << endl;
    return 0;
}

아이디어

누적 합을 이용했으며 (M x N) 크기의 체스판을 (K x K) 크기의 정사각 체스판으로 다시 만들어야 한다. 
1행 1열을 W으로 시작할지 B으로 시작할지에 따라 최솟값이 달라질 수 있기 때문에  먼저 B일 때와 W일 때,  2가지로 나눈다.
규칙에 맞게 1행 1열(i = 1, j = 1) 경우 부터  (i + j)%2의 조건으로 규칙에 맞는 색으로 칠해야하는지 아닌지 결정한다.
칠해야하는 경우라면 +1 을 해주어서 prefix_sum에 누적해주며 2중 반복문을 끝으로 누적 합이 계산된다.
이제 (K x K) 크기의 체스판을 최소로 칠할 수 있는 경우를 찾아야 하는데 이는 2번째 2중 반복문으로 일일이 순회하며 최솟값을 찾는다.