문제 해석
위의 사진과 같이 시작 색이 다를 수 있기 때문에, 그 경우도 잘 생각해서 알고리즘을 짜야한다.. (벌써 어려움...😂)
풀리지가 않아서 체스판 다시 칠하기영상을 보고 이해했다...
코드
import java.io.*;
import java.util.StringTokenizer;
public class Main {
public static int getMinCost(int startrow, int startcol, String[] chessboard) {
String[] board = {"WBWBWBWB", "BWBWBWBW"}; // 화이트 버전, 블랙버전
int whiteVerCount = 0; // 화이트를 기준으로 최소 비용을 고를 예정
for(int i = 0; i < 8; i++){ // 세로 8
int row = startrow + i; // 매개변수로 받은 chessboard의 값의 인덱스는 8X8이 아닌 전체 범위이기때문
for(int j = 0; j < 8; j++){ // 가로 8
int col = startcol + j;
if(chessboard[row].charAt(col) != board[row%2].charAt(j)){
whiteVerCount++;
}
}
}
// whiteVerCount는 하얀버전으로 체스판을 자를때의 최소비용이고 64 - whiteVerCount하면 블랙의 최소비용이다.
// 왜냐면, 체스판의 최대 크기가 8x8이기 때문
return Math.min(whiteVerCount, 64-whiteVerCount);
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken()); //세로줄 크기
int M = Integer.parseInt(st.nextToken()); // 가로줄 크기
String[] chessboard = new String[N];
for(int i = 0; i < N; i++){
chessboard[i] = br.readLine(); //한줄씩 입력받는 String형 배열
}
br.close();
int count = Integer.MAX_VALUE; //가장 큰값으로 지정해두고(초기값)
for(int i = 0; i <= N-8; i++){
for(int j = 0; j <= M-8; j++){
int resultCount = getMinCost(i, j, chessboard);
if(count > resultCount){
count = resultCount;
}
}
}
bw.write(count + "\n");
bw.flush();
bw.close();
}
}
코드 풀이
int count = Integer.MAX_VALUE;
for(int i = 0; i <= N-8; i++){ //8X8크기로 지정하기 위해 N-8만큼 반복
for(int j = 0; j <= M-8; j++){//8X8크기로 지정하기 위해 M-8만큼 반복
int resultCount = getMinCost(i, j, chessboard);
if(count > resultCount){
count = resultCount;
}
}
}
여기서 M-8을 한 이유는 (1) ~ (3)을 보면 차피 8x8이기 때문에 딱 M-8만큼 한 값 만큼만 반복하면 가로 끝까지 닿는다.
마찬가지로 N-8을 한 이유는 (3) ~ (8)을 보면 아래로 한칸씩 내려오는 것을 볼 수 있는데(가로는 움직임은 신경X) 보면 N-8만큼만 하면 세로 끝까지 닿는 것을 알 수 있다.
그래서 반복문도 가로 세로 각각 -8씩 해주었다.
그 다음에는 핵심 로직인데, 바로 최소 움직임(비용)을 찾는 로직 getMinCost()메소드이다.
인수로는 세로의 시작점, 가로의 시작점, 전체 입력받은 체스보드배열이 있다.
세로의 시작점과 가로의 시작점은 위에서 말했듯이, -8만큼 각각해줬기 때문에 그것을 기준으로 배열 탐색을 시작한다.
일단 흰색 버전은 WBWBWBWB이고, 블랙 버전은 BWBWBWBW라고 가정하자.
String[] board = {"WBWBWBWB", "BWBWBWBW"};
for(int i = 0; i < 8; i++){ // 세로 8
int row = startrow + i; // 매개변수로 받은 startrow(세로의 시작점)
for(int j = 0; j < 8; j++){ // 가로 8
int col = startcol + j; // 매개변수로 받은 startcol(가로의 시작점)
if(chessboard[row].charAt(col) != board[row%2].charAt(j)){
whiteVerCount++;
}
}
}
가로와 세로는 매개변수로 시작점을 받았기 때문에 그를 기준으로 인덱스를 늘려 탐색해야한다!아니면 배열에 모순이 생겨버림 => 그래서 만든 것이 row, col 변수이다.
그리고 나서 인수로 받은 전체 배열(NxM)의 해당 row의 col값과 board[row%2]
아무튼, board 배열의 row%2 인덱스의 요소의 j번째 문자가 다를 경우는 다시 칠해야하는 경우 이므로(패턴이 안맞다는 뜻!) 따로 뺐던 whiteVerCount(다시 칠해야하는 횟수를 저장하는 변수)를 증가시킨다.
블랙버전, 화이트버전 각각 탐색하여 값을 구해줘도 되지만, 최적의 효율인 코드를 짜기 위해 기준을 잡고 최소비용을 고르는 방법 설명한다.
어차피 체스 보드의 크기는 8x8이다. 그렇기 때문에 8x8(=다 바꾸는 경우가 8x8) - 하얀버전으로 시작한 다시 칠해야하는 횟수을 해주면 블랙버전일 경우의 바꿔야하는 횟수이다.
그 두개의 값을 비교해서 최솟값을 찾으면 최소 비용(횟수)의 값이 나온다.
return Math.min(whiteVerCount, 64-whiteVerCount);
결과
느낀점