[백준] 오목

김서연·2025년 2월 6일

코딩테스트

목록 보기
26/31
post-thumbnail

📜문제 설명

문제 바로가기

오목은 바둑판에 검은 바둑알과 흰 바둑알을 교대로 놓아서 겨루는 게임이다. 바둑판에는 19개의 가로줄과 19개의 세로줄이 그려져 있는데 가로줄은 위에서부터 아래로 1번, 2번, ... ,19번의 번호가 붙고 세로줄은 왼쪽에서부터 오른쪽으로 1번, 2번, ... 19번의 번호가 붙는다.

위의 그림에서와 같이 같은 색의 바둑알이 연속적으로 다섯 알을 놓이면 그 색이 이기게 된다. 여기서 연속적이란 가로, 세로 또는 대각선 방향 모두를 뜻한다. 즉, 위의 그림은 검은색이 이긴 경우이다. 하지만 여섯 알 이상이 연속적으로 놓인 경우에는 이긴 것이 아니다.

입력으로 바둑판의 어떤 상태가 주어졌을 때, 검은색이 이겼는지, 흰색이 이겼는지 또는 아직 승부가 결정되지 않았는지를 판단하는 프로그램을 작성하시오. 단, 검은색과 흰색이 동시에 이기거나 검은색 또는 흰색이 두 군데 이상에서 동시에 이기는 경우는 입력으로 들어오지 않는다.

📍입력

19줄에 각 줄마다 19개의 숫자로 표현되는데, 검은 바둑알은 1, 흰 바둑알은 2, 알이 놓이지 않는 자리는 0으로 표시되며, 숫자는 한 칸씩 띄어서 표시된다.

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 0 0 2 2 2 1 0 0 0 0 0 0 0 0 0 0
0 0 1 2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 2 2 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

📍출력

첫줄에 검은색이 이겼을 경우에는 1을, 흰색이 이겼을 경우에는 2를, 아직 승부가 결정되지 않았을 경우에는 0을 출력한다. 검은색 또는 흰색이 이겼을 경우에는 둘째 줄에 연속된 다섯 개의 바둑알 중에서 가장 왼쪽에 있는 바둑알(연속된 다섯 개의 바둑알이 세로로 놓인 경우, 그 중 가장 위에 있는 것)의 가로줄 번호와, 세로줄 번호를 순서대로 출력한다.

1
3 2

📄문제 해결

📝내가 푼 코드

(조건문 부분이 중간에 수정이 꽤 되었는데 기록해두지 않아서 최종 코드만..)

# PyPy3 메모리: 109544KB 시간: 92ms
# Python3 메모리: 32412KB 시간: 36ms

board = [list(map(int, input().split())) for _ in range(19)]
winner = None

for i in range(19):
    for j in range(19):
        # 돌이 있는지
        if board[i][j] > 0:
            # 가로 방향 검사(j: 14까지)
            if j <= 14 \
                and all(board[i][j] == board[i][j+k] for k in range(5)) \
                and not (j>0 and (board[i][j] == board[i][j-1])) \
                and not (j<14 and (board[i][j] == board[i][j+5])):

                # print("가로")
                winner = board[i][j]
                print(winner)
                print(i+1, j+1)
                break
            # 세로 방향 검사(i: 14까지)
            if i <= 14 \
                and all(board[i][j] == board[i+k][j] for k in range(5)) \
                and not (i>0 and (board[i][j] == board[i-1][j])) \
                and not (i<14 and (board[i][j] == board[i+5][j])):

                # print("세로")
                winner = board[i][j]
                print(winner)
                print(i+1, j+1)
                break
            # 대각선 오른쪽 위 방향 검사(i:4~18, j: 14까지)
            if i >= 4 and j <= 14 \
                and all(board[i][j] == board[i-k][j+k] for k in range(5)) \
                and not (i<18 and j>0 and (board[i][j] == board[i+1][j-1])) \
                and not (i>4 and j<14 and (board[i][j] == board[i-5][j+5])):

                # print("대각선 위")
                winner = board[i][j]
                print(winner)
                print(i+1, j+1)
                break
            # 대각선 아래, 세로 검사(i:0~14, j:14까지)
            if i <= 14 and j <= 14 \
                and all(board[i][j] == board[i+k][j+k] for k in range(5)) \
                and not (i>0 and j>0 and (board[i][j] == board[i-1][j-1])) \
                and not (i<14 and j<14 and (board[i][j] == board[i+5][j+5])):
            
                # print("대각선 아래")
                winner = board[i][j]
                print(winner)
                print(i+1, j+1)
                break
    if winner:
        break

if winner is None:
    print(0)

우선, 결과로 출력해야하는 좌표가 왼쪽 위이기 때문에 왼쪽 위를 시작점으로 보고 순회한다. 그리고 승리하는 경우는 총 네 가지(가로, 세로, 대각선 오른쪽 위 방향, 대각선 오른쪽 아래 방향)이고, 돌이 있는 경우 각 경우에 해당하는지 조건문을 활용해 검사한다.

검사 시에는 크게 세 가지를 검사한다.

if j <= 14 \
   and all(board[i][j] == board[i][j+k] for k in range(5)) \
   and not (j>0 and (board[i][j] == board[i][j-1])) \
   and not (j<14 and (board[i][j] == board[i][j+5]))

가로 방향 검사를 예시로 설명하겠다.

  1. IndexError가 발생하지 않도록 오목일 경우 가능한 인덱스 범위인지 확인한다.
    • j <= 14 : 19개의 줄 중에서 15번째 줄을 넘어가면 가로로 오목이 불가능하다.
  2. 연속하는 5개의 돌이 같은 색인지 확인한다.
    • all(board[i][j] == board[i][j+k] for k in range(5))
  3. 돌이 6개 이상 연속하는지 확인한다.
    • not (j>0 and (board[i][j] == board[i][j-1])) and not (j<14 and (board[i][j] == board[i][j+5])) : 5개의 돌이 연속할 경우 그 직선의 앞, 뒤에 있는 돌이 존재하는지, 존재한다면 색이 같은지 확인한다.

이 검사에서 통과할 경우 winner 변수에 승자가 저장되며, 결과값에 대한 출력도 이때 진행된다. winner 변수를 활용해 이중 반복문을 탈출하게 된다.

반복문을 모두 돌 때까지 승자를 찾지 못해 winner 변수가 여전히 Null일 경우에는 승자를 판별할 수 없다는 의미의 0을 결과값으로 출력한다.


🤔느낀점

사실 코드만 넓게 보면 그리 잘 작성한 코드인지는 모르겠다. 조건이 아주 많고, 조건을 통과할 경우 진행되는 코드가 4번 반복되기 때문이다. 이 부분은 어떻게든 중복을 없애볼까 하는 생각도 했지만 if문 하나에 조건문이 너무 길어지거나 불필요해보여서 일단은 그대로 유지하기로 결정했다.

이번에 문제를 풀면서 게시판과 다른 개발자 분들이 적어주신 반례에 너무 의존한다는 생각이 들었다. 생각해보면 이번 문제의 반례는 내가 직접 만들면서 풀어도 될 정도로 간단히 만들 수 있었기 때문이었다. 앞으로는 왜 안풀리는지 도무지 모르겠을 때는 바로 게시판에 들어가기보다 반례를 직접 만들어보는 등의 시도를 해보면 좋겠다는 생각이 들었다.

profile
가보자고! 🔥

0개의 댓글