[백준] 17780 - 새로운 게임 (Python)

2024. 5. 29. 10:28·알고리즘 (PS)/BOJ
반응형

요구사항에 맞게 구현하면 되는 문제

오랜만에 풀어서 그런지 꽤 어려웠다..

 

윷놀이 문제처럼 말을 이동하다가 다른 말을 만나면 쌓아야 하는데, 말을 쌓더라도 자신이 갖고있는 방향은 바뀌면 안된다.

 

class Unit:
    def __init__(self, number, row, col, direction):
        self.number = number
        self.row = row
        self.col = col
        self.direction = direction

    def check_next(self):
        if self.direction == RIGHT:
            return self.row, self.col + 1
        if self.direction == LEFT:
            return self.row, self.col - 1
        if self.direction == UP:
            return self.row - 1, self.col
        if self.direction == DOWN:
            return self.row + 1, self.col

    def change_direction(self):
        if self.direction == RIGHT:
            self.direction = LEFT
        elif self.direction == LEFT:
            self.direction = RIGHT
        elif self.direction == UP:
            self.direction = DOWN
        elif self.direction == DOWN:
            self.direction = UP

    def __str__(self):
        return str((self.number, self.row, self.col, self.direction))

 

먼저 말을 나타내는 Unit 클래스를 작성했다.

말을 쌓을 때는 이 Unit을 직접 쌓음으로서 자신들이 갖고 있는 방향이 유지되도록 하였다.

WHITE, RED, BLUE = 0, 1, 2
RIGHT, LEFT, UP, DOWN = 1, 2, 3, 4

N, K = map(int, input().split())
board_info = [[2 for _ in range(N+2)]] + [[2] + list(map(int, input().split())) + [2] for _ in range(N)] + [[2 for _ in range(N+2)]]
board = [[0 for _ in range(N+2)] for _ in range(N+2)]
unit_info = [[] for _ in range(K + 1)]
for i in range(1, K+1):
    row, col, direction = map(int, input().split())
    board[row][col] = i
    unit_info[i].append(Unit(i, row, col, direction))

 

보드판 타일색과 이동방향은 상수로 관리해준다.

보드판 바깥으로 나가는 것은 파란색으로 이동하는 것과 같다고 하였으니 보드판 주변을 파란색 타일로 둘러준다.

이렇게하면 1-index로 문제를 풀 수 있고, 이동할 때 범위를 벗어나는 걸 고려하지 않아도 되어서 좋다.

(다른 그래프 문제 풀 때도 애초에 이렇게 설정하고 풀어볼까..)

 

실제 말의 배치 현황을 나타내는 것은 board로, 각 보드의 타일색 정보는 board_info로 나눠 관리하였다.

unit_info 는 i번째 말 위에 어떤 말들이 쌓여있는지를 나타낸다.

처음에는 자신의 말 위에 자신에 대한 정보만 있도록 세팅하였다.

 

turn = 0
while turn <= 1000:
    turn += 1
    for unit_number in range(1, K+1):
        if unit_info[unit_number]:  # 현재 유닛이 움직일 수 있다면
            # 유닛이 보고있는 방향대로 이동
            now_unit: Unit = unit_info[unit_number][0]
            check_row, check_col = now_unit.check_next()

            if board_info[check_row][check_col] == WHITE:
                process_white(now_unit, check_row, check_col)

            elif board_info[check_row][check_col] == RED:
                process_red(now_unit, check_row, check_col)

            elif board_info[check_row][check_col] == BLUE:
                # 이동한 곳의 판이 파란색인 경우, 방향 바꾸고 파란색이 아니면 이동
                now_unit.change_direction()
                check_row, check_col = now_unit.check_next()
                if board_info[check_row][check_col] != BLUE:
                    if board_info[check_row][check_col] == WHITE:
                        process_white(now_unit, check_row, check_col)

                    elif board_info[check_row][check_col] == RED:
                        process_red(now_unit, check_row, check_col)

 

먼저 main 로직은 위와 같이 작성하였다.

현재 유닛이 다른 유닛 위에 올라가 있지 않다면, unit_info 의 자신 번호 데이터가 있다는 의미이다.

이때는 자신이 움직일 수 있다는 의미이므로, 이동하려는 방향의 타일색을 체크한다.

 

타일색이 하얀색, 빨간색일 때 처리하는 부분은 별도 함수로 분리하였다.

타일색이 파란색일 때, 한번 더 처리해야 하기 때문이다.

 

def process_white(now_unit, check_row, check_col):
    # 기존 말은 보드에서 지우기
    board[now_unit.row][now_unit.col] = 0
    if board[check_row][check_col] > 0:
        # 이동한 곳에 말이 이미 있는 경우, 이동하려는 유닛 위에 기존 말을 쌓기
        exist_unit = board[check_row][check_col]
        for unit in unit_info[unit_number]:
            unit_info[exist_unit].append(unit)
            unit.row = check_row
            unit.col = check_col
        unit_info[unit_number].clear()

        # 말이 4개 이상 쌓였다면 게임 종료
        if len(unit_info[exist_unit]) >= 4:
            print(turn)
            exit(0)
    else:
        # 이동한 곳에 말이 없는 경우, 쌓는 것 없이 이동만
        board[check_row][check_col] = now_unit.number
        for unit in unit_info[unit_number]:
            unit.row = check_row
            unit.col = check_col

 

먼저 하얀색을 처리할 때는 간단하다.

이동하려는 곳에 말이 있다면 현재 자신에게 쌓여있는 말들을 차례대로 순회하면서 이동하려는 말 위에 쌓아준다.

이때 말이 4개 이상 쌓였다면 그대로 턴을 출력하고 프로그램을 종료한다.

만약 이동하려는 곳에 말이 없다면 자신에게 쌓여있는 말 전체의 위치값을 바꿔주고 보드판에 반영하면 된다.

 

def process_red(now_unit, check_row, check_col):
    # 이동한 곳의 판이 빨간색인 경우
    board[now_unit.row][now_unit.col] = 0
    unit_number = now_unit.number
    # 이동한 곳에 말이 있는 경우, 기존 말의 배치를 뒤집고, 이동한 말 위에 추가
    if board[check_row][check_col] > 0:
        # 이동한 곳에 말이 이미 있는 경우, 이동하려는 유닛 위에 기존 말을 뒤집어서 쌓기
        exist_unit = board[check_row][check_col]
        while unit_info[unit_number]:
            popped_unit: Unit = unit_info[unit_number].pop()
            popped_unit.row = check_row
            popped_unit.col = check_col
            unit_info[exist_unit].append(popped_unit)

        # 말이 4개 이상 쌓였다면 게임 종료
        if len(unit_info[exist_unit]) >= 4:
            print(turn)
            exit(0)
    else:
        # 이동한 곳에 말이 없는 경우, 기존 말의 배치를 뒤집고 이동
        inverse_unit_number = unit_info[unit_number][-1].number
        if len(unit_info[unit_number]) > 1:
            while unit_info[unit_number]:
                popped_unit: Unit = unit_info[unit_number].pop()
                popped_unit.row = check_row
                popped_unit.col = check_col
                unit_info[inverse_unit_number].append(popped_unit)
        else:
            unit_info[unit_number][-1].row = check_row
            unit_info[unit_number][-1].col = check_col

        board[check_row][check_col] = inverse_unit_number

 

 

이동하려는 곳에 말이 있다면 자신의 말을 뒤에서부터 하나씩 빼면서 기존 말 위에 추가해준다.

이렇게하면 역순으로 쌓이게 된다.

 

이동하려는 곳에 말이 없다면 현재 쌓여있는 것을 반대로 뒤집어주고 위치만 이동한 곳으로 바꿔준다.

위 코드에서 뒤집으려는 말의 개수가 1개보다 큰지 체크하는 이유는 만약 뒤집으려는 말의 개수가 1개라면 반복문이 무한으로 돌기 때문이다.

 

정답 코드

class Unit:
    def __init__(self, number, row, col, direction):
        self.number = number
        self.row = row
        self.col = col
        self.direction = direction

    def check_next(self):
        if self.direction == RIGHT:
            return self.row, self.col + 1
        if self.direction == LEFT:
            return self.row, self.col - 1
        if self.direction == UP:
            return self.row - 1, self.col
        if self.direction == DOWN:
            return self.row + 1, self.col

    def change_direction(self):
        if self.direction == RIGHT:
            self.direction = LEFT
        elif self.direction == LEFT:
            self.direction = RIGHT
        elif self.direction == UP:
            self.direction = DOWN
        elif self.direction == DOWN:
            self.direction = UP

    def __str__(self):
        return str((self.number, self.row, self.col, self.direction))


WHITE, RED, BLUE = 0, 1, 2
RIGHT, LEFT, UP, DOWN = 1, 2, 3, 4

N, K = map(int, input().split())
board_info = [[2 for _ in range(N+2)]] + [[2] + list(map(int, input().split())) + [2] for _ in range(N)] + [[2 for _ in range(N+2)]]
board = [[0 for _ in range(N+2)] for _ in range(N+2)]
unit_info = [[] for _ in range(K + 1)]
for i in range(1, K+1):
    row, col, direction = map(int, input().split())
    board[row][col] = i
    unit_info[i].append(Unit(i, row, col, direction))


def process_white(now_unit, check_row, check_col):
    # 기존 말은 보드에서 지우기
    board[now_unit.row][now_unit.col] = 0
    if board[check_row][check_col] > 0:
        # 이동한 곳에 말이 이미 있는 경우, 이동하려는 유닛 위에 기존 말을 쌓기
        exist_unit = board[check_row][check_col]
        for unit in unit_info[unit_number]:
            unit_info[exist_unit].append(unit)
            unit.row = check_row
            unit.col = check_col
        unit_info[unit_number].clear()

        # 말이 4개 이상 쌓였다면 게임 종료
        if len(unit_info[exist_unit]) >= 4:
            print(turn)
            exit(0)
    else:
        # 이동한 곳에 말이 없는 경우, 쌓는 것 없이 이동만
        board[check_row][check_col] = now_unit.number
        for unit in unit_info[unit_number]:
            unit.row = check_row
            unit.col = check_col


def process_red(now_unit, check_row, check_col):
    # 이동한 곳의 판이 빨간색인 경우
    board[now_unit.row][now_unit.col] = 0
    unit_number = now_unit.number
    # 이동한 곳에 말이 있는 경우, 기존 말의 배치를 뒤집고, 이동한 말 위에 추가
    if board[check_row][check_col] > 0:
        # 이동한 곳에 말이 이미 있는 경우, 이동하려는 유닛 위에 기존 말을 뒤집어서 쌓기
        exist_unit = board[check_row][check_col]
        while unit_info[unit_number]:
            popped_unit: Unit = unit_info[unit_number].pop()
            popped_unit.row = check_row
            popped_unit.col = check_col
            unit_info[exist_unit].append(popped_unit)

        # 말이 4개 이상 쌓였다면 게임 종료
        if len(unit_info[exist_unit]) >= 4:
            print(turn)
            exit(0)
    else:
        # 이동한 곳에 말이 없는 경우, 기존 말의 배치를 뒤집고 이동
        inverse_unit_number = unit_info[unit_number][-1].number
        if len(unit_info[unit_number]) > 1:
            while unit_info[unit_number]:
                popped_unit: Unit = unit_info[unit_number].pop()
                popped_unit.row = check_row
                popped_unit.col = check_col
                unit_info[inverse_unit_number].append(popped_unit)
        else:
            unit_info[unit_number][-1].row = check_row
            unit_info[unit_number][-1].col = check_col

        board[check_row][check_col] = inverse_unit_number


turn = 0
while turn <= 1000:
    turn += 1
    for unit_number in range(1, K+1):
        if unit_info[unit_number]:  # 현재 유닛이 움직일 수 있다면
            # 유닛이 보고있는 방향대로 이동
            now_unit: Unit = unit_info[unit_number][0]
            check_row, check_col = now_unit.check_next()

            if board_info[check_row][check_col] == WHITE:
                process_white(now_unit, check_row, check_col)

            elif board_info[check_row][check_col] == RED:
                process_red(now_unit, check_row, check_col)

            elif board_info[check_row][check_col] == BLUE:
                # 이동한 곳의 판이 파란색인 경우, 방향 바꾸고 파란색이 아니면 이동
                now_unit.change_direction()
                check_row, check_col = now_unit.check_next()
                if board_info[check_row][check_col] != BLUE:
                    if board_info[check_row][check_col] == WHITE:
                        process_white(now_unit, check_row, check_col)

                    elif board_info[check_row][check_col] == RED:
                        process_red(now_unit, check_row, check_col)

print(-1)
반응형
저작자표시 비영리 변경금지 (새창열림)

'알고리즘 (PS) > BOJ' 카테고리의 다른 글

[백준] 17472 - 다리 만들기 2  (0) 2024.06.16
[백준] 1661 - 다솜이의 신발가게 (Python)  (1) 2024.05.29
[백준] 21609 - 상어 중학교 (Python)  (0) 2024.02.21
[백준] 20055 - 컨베이어 벨트 위의 로봇  (1) 2024.01.31
[백준] 3653 - 영화수집 (P4)  (0) 2023.11.17
'알고리즘 (PS)/BOJ' 카테고리의 다른 글
  • [백준] 17472 - 다리 만들기 2
  • [백준] 1661 - 다솜이의 신발가게 (Python)
  • [백준] 21609 - 상어 중학교 (Python)
  • [백준] 20055 - 컨베이어 벨트 위의 로봇
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (614)
      • 개인 프로젝트 (43)
        • 토이 프로젝트 (3)
        • [2020] 카카오톡 봇 (9)
        • [2021] 코드악보 공유APP (22)
        • [2022] 유튜브 뮤직 클론코딩 (9)
        • [2025] 한글 SQL 데이터베이스 (0)
      • 팀 프로젝트 (22)
        • [2020] 인공지능 숫자야구 (4)
        • [2022] OSAM 온라인 해커톤 (10)
        • [2024] GDSC 프로젝트 트랙 (6)
        • [2025] 큰소리 웹 페이지 (2)
      • CS (335)
        • 자료구조 (19)
        • 어셈블리 (41)
        • 멀티미디어응용수학 (7)
        • 컴퓨터 구조 (29)
        • 알고리즘 분석 (4)
        • 컴퓨터 네트워크 (38)
        • 프로그래밍언어론 (15)
        • HCI 윈도우즈프로그래밍 (26)
        • 기초데이터베이스 (29)
        • 운영체제 (23)
        • 오토마타 (24)
        • 문제해결기법 (11)
        • 블록체인 (22)
        • 소프트웨어공학 (21)
        • 기계학습심화 (12)
        • 컴퓨터그래픽스와 메타버스 (8)
        • 분산시스템특론 (6)
      • 자기계발 (44)
        • 생각 정리 (22)
        • 대외활동 (11)
        • 동아리 (7)
        • 자격증 (3)
        • 머니 스터디 (1)
      • 알고리즘 (PS) (107)
        • BOJ (101)
        • Programmers (5)
        • 알고리즘 이모저모 (1)
      • WEB(BE) (8)
        • express.js (1)
        • Spring & Spring Boot (7)
      • WEB(FE) (2)
        • html, css, js (1)
        • React.js (1)
      • Tool & Language (6)
        • Edit Plus (1)
        • Git (1)
        • Python3 (2)
        • Java (2)
      • Infra (12)
        • AWS (1)
        • Oracle Cloud (8)
        • Firebase (2)
        • Network (1)
      • Android (18)
        • Java (6)
        • Flutter (12)
      • Window (2)
        • Visual Studio 없이 WPF (1)
        • MFC (1)
      • 독서 (14)
        • Inside Javascript (7)
        • Database Internals (6)
        • 한 글 후기 (1)
  • 링크

    • github
    • website
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에버듀
[백준] 17780 - 새로운 게임 (Python)
상단으로

티스토리툴바