본문 바로가기
Problem Solving

[백준] 1018 체스판 다시 칠하기 python 알고리즘 문제

by DuncanKim 2022. 4. 22.
728x90

문제 1018. 체스판 다시 칠하기

 

1. 나의 코드와 발상 과정

 

n, m = map(int, input().split())
board_list = []
for i in range(n):   
    board_list.append(input())

# 연산(보드판을 일부 잘라 체크무늬로 칠해져 있는지 확인)
test_list = []
for i in range(n - 7): # 찾을 출발점
    for j in range(m - 7):
        first_W = 0
        first_B = 0
        for k in range(i, i + 8):
            for l in range(j, j + 8):
                if (k + l) % 2 == 0:
                    if board_list[k][l] != 'W':
                        first_W += 1
                    if board_list[k][l] != 'B':
                        first_B += 1
                else:
                    if board_list[k][l] != 'B':
                        first_W += 1
                    if board_list[k][l] != 'W':
                        first_B += 1
        test_list.append(min(first_W, first_B))

print(min(test_list))

이번 문제는 상당히 어려웠다. 

 

입력 부분만을 생각할 수 있었고 그 밑의 연산 라인들은 수입해온 것이다.

따라서 다시 공부해보고자 정말 오답노트다운 오답노트를 작성해볼 것이다.

 

연산 라인에서 내가 생각을 아예 못한 것은 아니다.

W 또는 B로 시작하는 문자열 라인을 기준으로 어떤 식을 작성해야겠다는 것은 알겠지만,

어떤 기준으로, 어떤 방식으로 구현해낼 지가 도저히 생각나지 않았다.

 

브루트포스 알고리즘이기 때문에 모든 경우의 수를 검토해야 하는데,

대체 8 * 8을 기준으로 입력된 라인들을 어떻게 잘라서, 어떤 조건문을 써서 접근해야 하는지에 대해

구글 선생님께 여쭤보았다.

 

답은 그렇게 어렵지는 않았다.

주어진 라인들을 바탕으로 해서

8 * 8씩 잘라서 모든 경우의 수를 검토해보면 되는 것이다.

 

(1) 입력 라인

# 입력
n, m = map(int, input().split())
board_list = []
for i in range(n):   
    board_list.append(input())

입력 라인은 위와 같다.

다만, 문자열 안에서 하나의 문자만 확인하면 되고, 파이썬은 문자열의 인덱스로 그 자리에 있는 문자를 추출할 수 있기 때문에 이차원 배열을 만들지 않았다.

 

(2) 연산 라인

test_list = []
for i in range(n - 7): # 찾을 출발점
    for j in range(m - 7):
        first_W = 0
        first_B = 0
        for k in range(i, i + 8):
            for l in range(j, j + 8):
                if (k + l) % 2 == 0:
                    if board_list[k][l] != 'W':
                        first_W += 1
                    if board_list[k][l] != 'B':
                        first_B += 1
                else:
                    if board_list[k][l] != 'B':
                        first_W += 1
                    if board_list[k][l] != 'W':
                        first_B += 1
        test_list.append(min(first_W, first_B))

구현하지 못한 라인이다.

답을 봐도 왜 반복문을 n - 7부터 시작하는지에 대해 의문이었다.

 

힌트는 체스판을 칠하는 경우의 수가 [a) 하얀색으로 시작하는 체스판 ,b) 검은색으로 시작하는 체스판] 두 가지라는 것이다.

 

(아래는 나의 이해과정)

Q. 그러면 어떤 기준으로 8 * 8로 잘라 원소의 개수를 판단할 것인가?

A. 조건이 틀리지 않으려면 전체 경우의 수를 모두 비교해보아야 한다. 모든 경우의 수로 잘라 보고 테스트 케이스마다의 다시 색칠해야 하는 횟수를 저장하여 비교해주어야 한다.

 

Q. 그렇다면 8 * 8은 어떻게 자를 것인가?

A. 이 경우 for문을 중첩하여 사용하면 된다. 

예시)

다음과 같은 입력 예시가 주어진다고 하자 (n = 10, m = 10)

B W B W B W B W B W
B W B W B W B W B W
B W B W B W B W B W
W B W B W B W B W B
W B W B W B W B W B
W B W B W B B B B B
B B B B B B B B B B
B B B B B B B B B B
B W W W W W W W W W
W W W B B B B B B B

1) 첫번째 반복 때 보아야 할 문자열

B W B W B W B W B W
B W B W B W B W B W
B W B W B W B W B W
W B W B W B W B W B
W B W B W B W B W B
W B W B W B B B B B
B B B B B B B B B B
B B B B B B B B B B
B W W W W W W W W W
W W W B B B B B B B

2) 두번째 반복 때 보아야 할 문자열

B W B W B W B W B W
B W B W B W B W B W
B W B W B W B W B W
W B W B W B W B W B
W B W B W B W B W B
W B W B W B B B B B
B B B B B B B B B B
B B B B B B B B B B
B W W W W W W W W W
W W W B B B B B B B

3) 세번째 반복 때 보아야 할 문자열

B W B W B W B W B W
B W B W B W B W B W
B W B W B W B W B W
W B W B W B W B W B
W B W B W B W B W B
W B W B W B B B B B
B B B B B B B B B B
B B B B B B B B B B
B W W W W W W W W W
W W W B B B B B B B

 

이렇게 보려면 board_list의 [i][j]부터 시작해서 [i+7][j+7]번째까지 확인하는 로직을 갖추어주면 된다.

그 코드는 다음과 같다.

 

## 입력받은 예시에서 테스트 할 문자열 만을 반복 연산하는 코드

test_list = []
for i in range(n - 7): # 찾을 출발점
    for j in range(m - 7):
        first_W = 0
        first_B = 0
        for k in range(i, i + 8):
            for l in range(j, j + 8):

i, j의 range가 n-7, m-7인 이유는 시작점이 인덱스 0부터 시작하기 때문이다.

위의 예시 중 3)의 예시처럼만 가면 되기 때문에 n - 7을 해준다. (안 해주면 out of range가 생긴다.)

 

 

그다음 for l in range(j, j + 8): 라인 아래에서 조건문을 돌려준다.

 

## W 또는 B가 먼저 시작하는 보드판으로 바꾸기 위해 색칠해야 하는 정사각형 개수를 세는 조건문

                if (k + l) % 2 == 0:
                    if board_list[k][l] != 'W':
                        first_W += 1
                    if board_list[k][l] != 'B':
                        first_B += 1
                else:
                    if board_list[k][l] != 'B':
                        first_W += 1
                    if board_list[k][l] != 'W':
                        first_B += 1
        test_list.append(min(first_W, first_B))

이게 제일 어려웠다.

W로 시작하는 때에는 어떻게 세고, B로 시작하는 때에는 어떻게 세고 하는 것이 생각하기 어려웠는데,

그렇게 하는 것이 아니라, 4중 for문으로 불러온 그 자리의 문자열을 기준으로 

처음이 W일 때 바꿔야 하는 횟수, B일 때 바꿔야 하는 횟수를 각각 세주면 된다. 이를 first_W, first_B 변수 안에 담아준다.

 

쉽게 말해

k, l은 불러온 8 * 8의 문자열에서 2차원 배열의 자리값과 같다.

board_list[k][l]가 board_list [0][0] 일 때,

WBWB... 를 갖추기 위해서는 W가 나와야 할 것이고, 만약 이때  B가 나온다면, first_W를 1씩 더해주어야 한다.

반대로 BWBW.... 를 갖추기 위해서는 B가 나와야 할 것이고, 만약 이때  W가 나온다면, first_B를 1씩 더해주어야 한다.

 

그런 다음 test_list에 min(first_W, first_B)를 해서 더 적게 나오는 수만 test list에 넣어준다.

 

 

(3) 출력

print(min(test_list))

test_list 중 가장 작은 값이 본 문제의 답이다.

print를 해준다.

 

 

 

2. 아쉬운

 

구현하는 실력이 그렇게 좋지 못하다는 것을 느꼈다.

실버 5 문제 마지막을 이 친구와 해서 다행이다.

4중 for문을 쓸 수도 있다는 것을 깊이 새겨본다.

 

 

 

 

 

 

문제출처

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

728x90

댓글