안녕하세요.
오늘은 백준 25682번: 체스판 다시 칠하기 2(링크) 문제를 풀어보려고 합니다.
문제
지민이는 자신의 저택에서 MN개의 단위 정사각형으로 나누어져 있는 M×N 크기의 보드를 찾았다. 어떤 정사각형은 검은색으로 칠해져 있고, 나머지는 흰색으로 칠해져 있다. 지민이는 이 보드를 잘라서 K×K 크기의 체스판으로 만들려고 한다.
체스판은 검은색과 흰색이 번갈아서 칠해져 있어야 한다. 구체적으로, 각 칸이 검은색과 흰색 중 하나로 색칠되어 있고, 변을 공유하는 두 개의 사각형은 다른 색으로 칠해져 있어야 한다. 따라서 이 정의를 따르면 체스판을 색칠하는 경우는 두 가지뿐이다. 하나는 맨 왼쪽 위 칸이 흰색인 경우, 하나는 검은색인 경우이다.
보드가 체스판처럼 칠해져 있다는 보장이 없어서, 지민이는 K×K 크기의 체스판으로 잘라낸 후에 몇 개의 정사각형을 다시 칠해야겠다고 생각했다. 당연히 K×K 크기는 아무데서나 골라도 된다. 지민이가 다시 칠해야 하는 정사각형의 최소 개수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 정수 N, M, K가 주어진다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.
출력
첫째 줄에 지민이가 잘라낸 K×K 보드를 체스판으로 만들기 위해 다시 칠해야 하는 정사각형 개수의 최솟값을 출력한다.
예제 입력
//1
4 4 3
BBBB
BBBB
BBBW
BBWB
//2
8 8 8
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBBBWBW
WBWBWBWB
BWBWBWBW
WBWBWBWB
BWBWBWBW
//3
10 13 10
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
BBBBBBBBWBWBW
BBBBBBBBBWBWB
WWWWWWWWWWBWB
WWWWWWWWWWBWB
//4
9 23 9
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBWWWWWWWW
예제 출력
//1
2
//2
1
//3
30
//4
40
제한
시간 제한: 1초
메모리 제한: 256MB
풀이
체스판 다시 칠하기 문제를 먼저 풀고오면 이해가 더 쉽다.
위 문제와 동일하게 맨 왼쪽 위 칸이 흰색(W)이거나 검은색(B)인 경우에 따라 나눠주면 된다.
그러나 모든 경우를 일일히 다 계산하기엔 시간복잡도가 O(N^3)이 나오기에 더 효율적인 방법을 써야한다.
이때 누적합을 사용하면 쉽게 풀린다.
누적합을 이용해 맨 처음 칸이 W일 때, 맨 왼쪽 위부터 (i,j)까지의 다시 칠해야 하는 칸의 개수를 누적하여 구한다. 마찬가지로 맨 처음 칸이 B일 때도 동일하게 진행한다.
구하는 방법은 다음과 같이 누적합 사각형을 더하고 빼는 식이다.
이후 i=0~n-k, j=0~n-k에 대해 K * K 크기의 보드칸을 검사해야한다.
이전에 구한 누적합을 사용하면 (i,j) 부터 (i+k,j+k)까지의 보드칸의 다시 칠해야 하는 칸의 개수를 구할 수 있다.
위에서 누적합을 구한 것처럼 누적합 사각형을 더하고 빼주면 구간합을 구할 수 있게 된다.
정답
코드에서는 예외처리를 쉽게 하기 위해 dp배열을 1부터 시작하도록 수정했다.
더보기
#include <iostream>
using namespace std;
using ll = long long;
int n, m, k;
char arr[2001][2001];
int dp[2][2001][2001] = { 0 };
// dp[0]: 맨 처음이 B일 때 고쳐야 되는 개수
// dp[1]: 맨 처음이 W일 때 고쳐야 되는 개수
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> arr[i][j];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int idx = (i + j) % 2;
int ba = (arr[i][j] == 'B');
int wa = (arr[i][j] == 'W');
dp[0][i+1][j+1] = dp[0][i+1][j] + dp[0][i][j+1] - dp[0][i][j] + (idx == 0 ? wa : ba);
dp[1][i+1][j+1] = dp[1][i+1][j] + dp[1][i][j+1] - dp[1][i][j] + (idx == 0 ? ba : wa);
}
}
int ans = 2000*2000+2000;
for (int i = 0; i <= n - k; i++) {
for (int j = 0; j <= m - k; j++) {
int fb = dp[0][i + k][j + k] - dp[0][i][j + k] - dp[0][i + k][j] + dp[0][i][j];
int fw = dp[1][i + k][j + k] - dp[1][i][j + k] - dp[1][i + k][j] + dp[1][i][j];
int tmp = min(fb, fw);
ans = min(ans, tmp);
}
}
cout << ans;
return 0;
}
이렇게 해서 백준 25682번 체스판 다시 칠하기 2문제를 풀어보았습니다.
댓글로 질문을 남기시면 성실히 답해드리겠습니다
감사합니다!
'PS > 백준 문제' 카테고리의 다른 글
[BOJ][C++] 10830번 행렬 제곱 (0) | 2024.10.14 |
---|---|
[BOJ][C++] 7579번 앱 (2) | 2024.10.13 |
[BOJ][C++] 11049번 행렬 곱셈 순서 (0) | 2024.10.10 |
[BOJ][C++] 2437번 저울 (0) | 2024.05.04 |
[BOJ][C++] 11403번 경로 찾기 (0) | 2024.03.14 |