[백준] 21609 - 상어 중학교 (Python)

2024. 2. 21. 23:13·알고리즘 (PS)/BOJ
반응형

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

 

21609번: 상어 중학교

상어 중학교의 코딩 동아리에서 게임을 만들었다. 이 게임은 크기가 N×N인 격자에서 진행되고, 초기에 격자의 모든 칸에는 블록이 하나씩 들어있고, 블록은 검은색 블록, 무지개 블록, 일반 블록

www.acmicpc.net

 

BFS 를 활용하는 시뮬레이션 문제이다.

구현할 때 모든 조건을 꼼꼼히 살펴야하고, 작은 사소한 실수 하나로도 문제를 틀리기 때문에 난이도가 높다.

N, M = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(N)]
score = 0
while True:
    x, y, size = find_block_group()
    if size < 2:
        break

    delete_block_group(x, y)
    score += (size * size)
    
    set_gravity()
    
    rotate()
    
    set_gravity()


print(score)

 

이런 복잡한 문제는 큰 틀부터 잡은 다음, 디테일을 구현해나가는 것이 구현하기 편하다.

먼저 find_block_group() 함수부터 구현했다.

 

def find_block_group():
    _x, _y, max_size = None, None, 0
    max_rainbow_block = 0
    visited = [[False for _ in range(N)] for _ in range(N)]
    for i in range(N):
        for j in range(N):
            if board[i][j] in (0, -1) or visited[i][j]:
                continue
            d = deque()
            d.append((i, j))
            color = board[i][j]
            size = 1
            visited[i][j] = True
            rainbow_block = 0
            while d:
                now_i, now_j = d.popleft()
                for k in range(4):
                    next_i, next_j = now_i + di[k], now_j + dj[k]
                    if 0 <= next_i < N and 0 <= next_j < N:
                        if not visited[next_i][next_j] and board[next_i][next_j] in (color, 0):
                            if board[next_i][next_j] == 0:
                                rainbow_block += 1
                            d.append((next_i, next_j))
                            visited[next_i][next_j] = True
                            size += 1

            if size > max_size:
                max_size = size
                _x, _y = i, j
            elif size == max_size:
                if rainbow_block > max_rainbow_block:
                    max_rainbow_block = rainbow_block
                    _x, _y = i, j
                elif rainbow_block == max_rainbow_block:
                    _x, _y = i, j # 나중에 탐색한 게 기준 블록의 행/열이 더 크다.

            # 무지개 블록의 방문 초기화
            for _i in range(N):
                for _j in range(N):
                    if board[_i][_j] == 0:
                        visited[_i][_j] = False

    return _x, _y, max_size

 

이 코드는 내가 맞왜틀을 했던 코드이다.

이 코드에서 단 한줄을 추가하지 않아서 제출시 0%에서 틀렸다.

if size > max_size:
    max_size = size
    _x, _y = i, j
elif size == max_size:
    if rainbow_block > max_rainbow_block:
        max_rainbow_block = rainbow_block
        _x, _y = i, j

 

바로 이 부분이 잘못되었다.

블록 그룹에서 우선순위를 판단할 때는 블록 그룹의 크기, 블록 그룹 내 무지개 블록의 개수가 사용된다.

이때 블록 그룹의 크기를 가지고 판단을 한 상황에서 "무지개 블록의 개수가 바뀌지 않고 있다."

그래서 아래와 같이 한 줄의 코드를 추가해주면 된다.

if size > max_size:
    max_size = size
    max_rainbow_block = rainbow_block
    _x, _y = i, j
elif size == max_size:
    if rainbow_block > max_rainbow_block:
        max_rainbow_block = rainbow_block
        _x, _y = i, j

 

이 함수는 조건에 맞는 블록 그룹의 크기, 행, 열 좌표를 반환한다.

만약 이 함수가 반환한 블록 그룹의 크기가 1 이하라면 지울 수 있는 블록 그룹이 없으므로 반복문을 빠져나간다.

 

이제 지울 수 있는 블록 그룹이 있을 때, 블록 그룹을 지우는 함수를 작성하자.

def delete_block_group(x, y):
    color = board[x][y]
    d = deque()
    d.append((x, y))
    board[x][y] = -2
    while d:
        now_i, now_j = d.popleft()
        for k in range(4):
            next_i, next_j = now_i + di[k], now_j + dj[k]
            if 0 <= next_i < N and 0 <= next_j < N:
                if board[next_i][next_j] in (color, 0):
                    d.append((next_i, next_j))
                    board[next_i][next_j] = -2

 

삭제할 블록 그룹의 기준 블록 좌표를 입력하면, 그 좌표에서부터 다시 BFS를 돌면서 모든 블럭을 지우는 코드이다.

 

이 함수를 구현할 때도 한가지 실수한 부분이 있었다.

BFS 를 구현할 때 방문체크 (이 함수에서는 블록을 지웠다는 표시로 -2를 하면서 체크) 는 큐에 넣으면서 체크해야한다.

큐에서 꺼내면서 체크하면 안된다.

 

하지만 난 이 당연한 사실을 알면서도 큐에서 꺼내면서 체크를 하는 실수를 저지르고 말았다..

visit 배열을 쓰지 않고 구현해서 낯설었던 모양이다.

 

다음으로 중력을 적용하는 함수를 구현했다.

def set_gravity():
    for j in range(N):
        floor = N-1
        for i in range(N-1, -1, -1):
            if board[i][j] == -1:
                floor = i-1
            elif board[i][j] >= 0:
                if i != floor:
                    board[floor][j] = board[i][j]
                    board[i][j] = -2
                floor -= 1

 

아래에서부터 위로 훑으면서 내릴 수 있는 블럭을 만날 때마다, 바닥 좌표로 해당 블럭을 옮겨주고, 바닥 좌표를 해당 블럭 위로 바꿔주었다.

 

마지막으로 반시계방향 90도 회전 코드를 작성하였다.

def rotate():
    copied_board = [[board[i][j] for j in range(N)] for i in range(N)]
    for i in range(N):
        for j in range(N):
            board[N-1-j][i] = copied_board[i][j]

 

프로그래밍 언어에서 2차원 배열을 배울 때 많이 하던 그 작업이다.

 

전체 코드는 아래와 같다.

from collections import deque
di = [-1, 0, 0, 1]
dj = [0, -1, 1, 0]


def find_block_group():
    _x, _y, max_size = None, None, 0
    max_rainbow_block = 0
    visited = [[False for _ in range(N)] for _ in range(N)]
    for i in range(N):
        for j in range(N):
            if board[i][j] in (0, -1, -2) or visited[i][j]:
                continue
            d = deque()
            d.append((i, j))
            color = board[i][j]
            size = 1
            visited[i][j] = True
            rainbow_block = 0
            while d:
                now_i, now_j = d.popleft()
                for k in range(4):
                    next_i, next_j = now_i + di[k], now_j + dj[k]
                    if 0 <= next_i < N and 0 <= next_j < N:
                        if not visited[next_i][next_j] and board[next_i][next_j] in (color, 0):
                            if board[next_i][next_j] == 0:
                                rainbow_block += 1
                            d.append((next_i, next_j))
                            visited[next_i][next_j] = True
                            size += 1

            if size > max_size:
                max_size = size
                max_rainbow_block = rainbow_block
                _x, _y = i, j
            elif size == max_size:
                if rainbow_block > max_rainbow_block:
                    max_rainbow_block = rainbow_block
                    _x, _y = i, j
                elif rainbow_block == max_rainbow_block:
                    _x, _y = i, j # 나중에 탐색한 게 기준 블록의 행/열이 더 크다.

            # 무지개 블록의 방문 초기화
            for _i in range(N):
                for _j in range(N):
                    if board[_i][_j] == 0:
                        visited[_i][_j] = False

    return _x, _y, max_size


def delete_block_group(x, y):
    color = board[x][y]
    d = deque()
    d.append((x, y))
    board[x][y] = -2
    while d:
        now_i, now_j = d.popleft()
        for k in range(4):
            next_i, next_j = now_i + di[k], now_j + dj[k]
            if 0 <= next_i < N and 0 <= next_j < N:
                if board[next_i][next_j] in (color, 0):
                    d.append((next_i, next_j))
                    board[next_i][next_j] = -2


def set_gravity():
    for j in range(N):
        floor = N-1
        for i in range(N-1, -1, -1):
            if board[i][j] == -1:
                floor = i-1
            elif board[i][j] >= 0:
                if i != floor:
                    board[floor][j] = board[i][j]
                    board[i][j] = -2
                floor -= 1


def rotate():
    copied_board = [[board[i][j] for j in range(N)] for i in range(N)]
    for i in range(N):
        for j in range(N):
            board[N-1-j][i] = copied_board[i][j]


N, M = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(N)]

score = 0
while True:
    x, y, size = find_block_group()
    if size < 2:
        break

    delete_block_group(x, y)

    score += (size * size)

    set_gravity()

    rotate()

    set_gravity()

print(score)

 

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

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

[백준] 1661 - 다솜이의 신발가게 (Python)  (1) 2024.05.29
[백준] 17780 - 새로운 게임 (Python)  (1) 2024.05.29
[백준] 20055 - 컨베이어 벨트 위의 로봇  (1) 2024.01.31
[백준] 3653 - 영화수집 (P4)  (0) 2023.11.17
[백준] 2243 - 사탕상자 (P5)  (0) 2023.11.16
'알고리즘 (PS)/BOJ' 카테고리의 다른 글
  • [백준] 1661 - 다솜이의 신발가게 (Python)
  • [백준] 17780 - 새로운 게임 (Python)
  • [백준] 20055 - 컨베이어 벨트 위의 로봇
  • [백준] 3653 - 영화수집 (P4)
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (614) N
      • 개인 프로젝트 (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) N
        • express.js (1)
        • Spring & Spring Boot (7) N
      • 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
에버듀
[백준] 21609 - 상어 중학교 (Python)
상단으로

티스토리툴바