[Samsung] 2021-2 오전 2번 색깔 폭탄

Question

Language: Python

Difficulty: Gold 2

해당 문제에서 구현해야될 부분은 크게 4가지이다.

  1. 가장 큰 폭탄 묶음 찾기
  2. 폭탄 제거
  3. 중력 작용
  4. 반시계 회전

각 세부 과정은 아래와 같다

  1. 가장 큰 폭탄 묶음 찾기 각 좌표에 대해 인접한 좌표의 색깔을 비교해서 같은 색이거나 빨간색 인경우 폭탄 묶음에 포함시킨다. 각각의 폭탄 묶음을 처리할 때 기준점이 되는 좌표 또한 갱신한다. 추가적으로 빨간색 폭탄의 경우 따로 배열에 저장하여 나중에 활용할 수 있도록 한다.

같은 색깔, 혹은 빨간색 폭탄에 대한 폭탄 묶음을 구하고 난 이후, 폭탄 묶음이 유효한지 여부를 판단해야한다. 폭탄 묶음 내 폭탄의 갯수가 2개 이상이어야 하며 빨간색으로만 이루어지지 않았는지 판단한다. 빨간색 폭탄의 경우 모든 색깔의 폭탄에 대해서 공유가 가능하기 때문에 하나의 폭탄에 대한 폭탄 묶음을 처리하고 난 이후에는 방문해제 처리를 해줘야 다른 폭탄도 처리가 가능하다.

이후, 폭탄 묶음에 대한 배열에 대해 정렬을 수행한다. 폭탄의 묶음 내 폭탄의 갯수가 가장 많은순, 빨간색 폭탄의 갯수가 가장 적은 순, 기준점의 행이 큰 순, 열이 작은순으로 정렬을 수행해서 가장 우선순위가 높은 폭탄 묶음을 찾아낸다.

def find_components():
    visited=[[False] * n for _ in range(n)]

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

    components=[]

    for start_row in range(n):
        for start_col in range(n):
            color=board[start_row][start_col]
            #이전에 방문하였거나 빨간색인 경우 처리하지 않는다.
            if visited[start_row][start_col] or color <=0 :
                continue
            
            queue=deque([(start_row,start_col)])

            #기준점
            base_row,base_col=start_row,start_col

            #빨간점들의 좌표
            red_coordinates=[]
            red_count=0

            component=[]
            component_count=0

            while queue:
                row,col=queue.popleft()
                #이전에 방문했는지 검사
                if visited[row][col]:
                    continue
                visited[row][col]=True

                component.append((row,col))
                component_count+=1
                #빨간색인 경우 따로 배열에 추가로 관리한다.
                if board[row][col]==0:
                    red_coordinates.append((row,col))
                    red_count+=1
                
                #빨간색이 아닌 점의 경우기준점 갱신
                elif row > base_row or (row == base_row and col < base_col):
                    base_row,base_col=row,col

                for dir in range(4):
                    next_row=row+dy[dir]
                    next_col=col+dx[dir]

                    #좌표의 범위 검증, 빈칸 검증, 색깔 검증
                    if not check_range(next_row,next_col) or board[next_row][next_col] == -2 or (board[next_row][next_col] != color and board[next_row][next_col] != 0):
                        continue
                    
                    queue.append((next_row,next_col))
    
            #폭탄 묶음의 크기는 무조건 2이상이어야 하며, 빨간색으로만 이루어져서는 안된다.    
            if component_count >=2 and component_count != red_count:
                components.append((component_count,red_count,base_row,base_col,component))

            #빨간색 폭탄의 경우 공용으로 사용되기 때문에 방문 해제 처리를 해야한다.
            for row,col in red_coordinates:
                visited[row][col]=False
    
    components.sort(key=lambda x: (-x[0],x[1],-x[2],x[3]))
    return components
  1. 폭탄 제거

폭탄 묶음 내에 폭탄을 제거하고 해당 자리는 -2를 삽입해서 빈칸임을 표시한다. 폭탄을 제거한 갯수의 제곱 만큼을 점수로 반환한다.

#폭탄 묶음 제거
def explosion(board,component):
    count=0
    for row, col in component:
        board[row][col]=-2
        count+=1
    
    return count**2
  1. 중력 작용

검은색 돌이 아닌 폭탄에 대해 아래에 빈칸이 있는 경우 내릴 수 있을 만큼 계속 내린다.

#중력의 작용
def gravity(board):
    
    for col in range(n):
        for row in range(n-1,-1,-1):

            #검은색 돌이거나, 빈칸 인경우 중력이 작용하지 않는다.
            if board[row][col]==-1 or board[row][col] == -2:
                continue
            
            #아래의 행이 빈칸 인 경우에만 이동하도록 한다.
            target_row=row
            while target_row < n-1 and board[target_row+1][col] ==-2:
                target_row+=1
            
            #이동한 경우에만 이동을 반영한다.
            if target_row != row:
                board[target_row][col]=board[row][col]
                board[row][col]=-2
  1. 반시계 회전

반시계 회전의 경우 좌표의 평행이동을 고려하여 새로운 배열을 반환하는 작업을 진행한다.

#반시계 방향 회전
def rotate_counter_clockwise(board):
    new_board=[[-2] * n for _ in range(n)]
    
    for row in range(n):
        for col in range(n):
            new_board[n-col-1][row]=board[row][col]

    for row in range(n):
        for col in range(n):
            board[row][col]=new_board[row][col]

Solution

from collections import deque

#폭탄 묶음을 찾는 함수
def find_components():
    visited=[[False] * n for _ in range(n)]

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

    components=[]

    for start_row in range(n):
        for start_col in range(n):
            color=board[start_row][start_col]
            #이전에 방문하였거나 빨간색인 경우 처리하지 않는다.
            if visited[start_row][start_col] or color <=0 :
                continue
            
            queue=deque([(start_row,start_col)])

            #기준점
            base_row,base_col=start_row,start_col

            #빨간점들의 좌표
            red_coordinates=[]
            red_count=0

            component=[]
            component_count=0

            while queue:
                row,col=queue.popleft()
                #이전에 방문했는지 검사
                if visited[row][col]:
                    continue
                visited[row][col]=True

                component.append((row,col))
                component_count+=1
                #빨간색인 경우 따로 배열에 추가로 관리한다.
                if board[row][col]==0:
                    red_coordinates.append((row,col))
                    red_count+=1
                
                #빨간색이 아닌 점의 경우기준점 갱신
                elif row > base_row or (row == base_row and col < base_col):
                    base_row,base_col=row,col

                for dir in range(4):
                    next_row=row+dy[dir]
                    next_col=col+dx[dir]

                    #좌표의 범위 검증, 빈칸 검증, 색깔 검증
                    if not check_range(next_row,next_col) or board[next_row][next_col] == -2 or (board[next_row][next_col] != color and board[next_row][next_col] != 0):
                        continue
                    
                    queue.append((next_row,next_col))
    
            #폭탄 묶음의 크기는 무조건 2이상이어야 하며, 빨간색으로만 이루어져서는 안된다.    
            if component_count >=2 and component_count != red_count:
                components.append((component_count,red_count,base_row,base_col,component))

            #빨간색 폭탄의 경우 공용으로 사용되기 때문에 방문 해제 처리를 해야한다.
            for row,col in red_coordinates:
                visited[row][col]=False
    
    components.sort(key=lambda x: (-x[0],x[1],-x[2],x[3]))
    return components

#폭탄 묶음 제거
def explosion(board,component):
    count=0
    for row, col in component:
        board[row][col]=-2
        count+=1
    
    return count**2

#중력의 작용
def gravity(board):
    
    for col in range(n):
        for row in range(n-1,-1,-1):

            #검은색 돌이거나, 빈칸 인경우 중력이 작용하지 않는다.
            if board[row][col]==-1 or board[row][col] == -2:
                continue
            
            #아래의 행이 빈칸 인 경우에만 이동하도록 한다.
            target_row=row
            while target_row < n-1 and board[target_row+1][col] ==-2:
                target_row+=1
            
            #이동한 경우에만 이동을 반영한다.
            if target_row != row:
                board[target_row][col]=board[row][col]
                board[row][col]=-2

 
#반시계 방향 회전
def rotate_counter_clockwise(board):
    new_board=[[-2] * n for _ in range(n)]
    
    for row in range(n):
        for col in range(n):
            new_board[n-col-1][row]=board[row][col]

    for row in range(n):
        for col in range(n):
            board[row][col]=new_board[row][col]

#좌표의 범위 검증
def check_range(row,col):
    if row < 0 or row>=n or col < 0 or col>=n:
        return False
    return True

def solution():
    total_score=0
    
    while True:
        components=find_components()
        #더 이상 폭탄 묶음이 없는 경우에는 종료
        if len(components)==0:
            break
        
        #가장 우선순위가 큰 폭탄 묶음 제거
        score=explosion(board,components[0][4])
        total_score+=score
        
        #중력 작용
        gravity(board)
        
        #반시계 방향 회전
        rotate_counter_clockwise(board)

        #중력 작용
        gravity(board)
 
    print(total_score)

if __name__ =="__main__":
    n,m=map(int,input().split())
    board=[list(map(int,input().split())) for _ in range(n)]

    solution()

댓글남기기