Skip to content

(22 복습) 삼성 SW 역량 테스트 기출 문제 / 백준 / 21609번: 상어 중학교 : 빡구현X3 (BFS + 중력 + 회전) #144

@sallyy1

Description

@sallyy1

image

  • BFS 탐색 (그룹들 구하기)
  • 배열에 중력 작용해 떨어뜨리기
  • 배열 90도 반시계 회전
# 블록의 종류
# 검은색 / 무지개 / 일반(M 가지 색상)
#  -1     0         1 ~ M

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]


n, m = map(int, input().split())
a = [list(map(int, input().split())) for _ in range(n)]

# for line in a:
#     print(line)

# 블록 그룹 : 연결된 블록의 집합
# <조건>
# 1. 일반 블록이 적어도 1개 (색은 모두 같아야 함 !!)
# 2. 검은색 블록 있으면 X
# (3. 무지개 블록은 얼마나 들어있든 상관없다.)

# 4. 그룹에 속한 블록의 개수는 2보다 크거나 같아야 (-> len(그룹) == 1 이면 continue ??)

# <기준 블록> : 무지개 블록(0) 이 아닌 블록 중에서 행의 번호가 가장 작은 블록 => 열의 번호가 가장 작은 블록


## 왼쪽 반시계 90도 수행
def rotate(a):
    n = len(a)
    m = len(a[0])

    b = [[0]*n for _ in range(m)]

    for i in range(m):
        for j in range(n):
            b[i][j] = a[j][m - i - 1]

    return b



## 오토 플레이 수행 (블록 그룹이 존재하지 않으면 게임 종료)
from collections import deque

score = 0
colors = [num for num in range(1, m+1)]

turn = 0

while True:
    ok = False
    turn += 1
    ## 1 단계. 크기가 가장 큰 블록 그룹 찾기
    ### (블록 그룹의 크기 up / 무지개 블록의 개수 up / 기준 블록의 행 up / 기준 블록의 열 up)
    ###     len(temp)           cnt               row               col
    check = [[False]*n for _ in range(n)]
    groups = []

    # for line in check:
    #     print(line)

    for i in range(n):
        for j in range(n):
            if a[i][j] in [num for num in range(1, m+1)] and check[i][j] == False:
                # 해당 블록을 기준으로 탐색 시작
                check[i][j] = True
                idx = a[i][j] # 해당 블록의 색깔 번호
                q = deque([(i, j)])

                temp = []
                temp.append((i, j))

                cnt = 0
                row = i
                col = j

                while q:
                    x, y = q.popleft()

                    for k in range(4):
                        nx, ny = x+dx[k], y+dy[k]
                        if 0<=nx<n and 0<=ny<n:
                            if a[nx][ny] == -1: # 검은색 블록
                                continue

                            if a[nx][ny] == idx and check[nx][ny] == False: # 일반 블록
                                q.append((nx, ny))
                                check[nx][ny] = True ###
                                temp.append((nx, ny))
                                # <기준 블록> : 무지개 블록(0) 이 아닌 블록 중에서 행의 번호가 가장 작은 블록 => 열의 번호가 가장 작은 블록
                                if row > nx: row = nx
                                if col > ny: col = ny


                            elif a[nx][ny] == 0 and (nx, ny) not in temp: # (아직 해당 그룹에서는 방문한 적 없는) 무지개 블록
                                q.append((nx, ny))
                                temp.append((nx, ny))
                                cnt += 1 ###


                if len(temp) >= 2:
                    ok = True
                    groups.append([len(temp), cnt, row, col, temp])



    if ok == False:
        break

    ## 2 단계. 해당 블록 그룹의 모든 블록 제거 + len(그룹) ** 2 만큼 점수획득
    groups.sort(reverse = True)
    best_group = groups[0]
    score += (best_group[0] ** 2)

    for (remove_x, remove_y) in best_group[4]:
        a[remove_x][remove_y] = -8 # 제거



    # for line in a:
    #     print(line)


    ## (격자에 중력이 작용하면 검은색 블록을 제외한 모든 블록이 행의 번호가 큰 칸으로 이동한다.
    #                       이동은 다른 블록이나 격자의 경계를 만나기 전까지 계속 된다.)
    ## 3 단계. 격자에 중력 작용
    for col in range(0, n):
        ## <1. 이동 대상(블록) 선정>
        row = n-1 ###
        while row > -1:
            ##if col == 0: print(row, col, n, a[row][col])
            if a[row][col] == -1 or a[row][col] == -8: # 검은색 or 빈칸
                row -= 1
                continue

            else:
                ## <2. 중력 이동 수행>
                idx = row ### (이동시킬 블록의 행 실시간 위치)

                for standard in range(row+1, n):
                    if a[standard][col] != -8: # 이동할 칸에 다른 블록이 있으면(=빈칸이 아니면)
                        break

                    else: # SWAP
                        a[standard][col], a[idx][col] = a[idx][col], a[standard][col]
                        idx += 1

                row -= 1


    # print('### 결과 ###')
    # for line in a:
    #     print(line)





    ## 4 단계. 격자 90도 반시계 회전
    a = rotate(a)
    # print('### 결과 ###')
    # for line in a:
    #     print(line)


    ## 5 단계. 격자에 다시 중력 작용
    for col in range(0, n):
        ## <1. 이동 대상(블록) 선정>
        row = n-1 ###
        while row > -1:
            ##if col == 0: print(row, col, n, a[row][col])
            if a[row][col] == -1 or a[row][col] == -8: # 검은색 or 빈칸
                row -= 1
                continue

            else:
                ## <2. 중력 이동 수행>
                idx = row ### (이동시킬 블록의 행 실시간 위치)

                for standard in range(row+1, n):
                    if a[standard][col] != -8: # 이동할 칸에 다른 블록이 있으면(=빈칸이 아니면)
                        break

                    else: # SWAP
                        a[standard][col], a[idx][col] = a[idx][col], a[standard][col]
                        idx += 1

                row -= 1


    # print('### 결과 ###')
    # for line in a:
    #     print(line)



print(score)



Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions