BFS, 시뮬레이션 - 고대 문명 유적 탐사

jisu_log·2025년 9월 22일

알고리즘 문제풀이

목록 보기
102/105

  • 꼭 알아야 할 코드:

    1) 시계방향 90도 회전
    output_arr = list(map(list, zip(*input_arr[::-1])))
    2) 180도 회전
    output_arr = [a[::-1] for a in input_arr[::-1]]
    3) 270도 회전
    output_arr = [x[::-1] for x in list(map(list, zip(*input_arr[::-1])))[::-1]]
  • 코드 실수 주의

    1) sort할 때 행 번호 큰 순인데 작은 순으로 잘못 봄! 조건 잘 보기
    2) 탐사 진행하고 최대 유물인 경우 찾았는데 그 때의 회전한 맵을 현재 maps에 적용 안했음
    3) BFS 할 때 같은 유적끼리 연결된 덩어리 카운트 하려면, group 리스트에 BFS 하면서 방문한 좌표를 모두 모아서 그 길이를 세어서 그걸로 연결된 조각 개수를 처리해야 함!
from collections import deque

K, M = map(int, input().split())
maps = []

for i in range(5):
    line = list(map(int, input().split()))
    maps.append(line)

wall = list(map(int, input().split()))

wall_idx = 0

# 중심 좌표(x, y) 기준으로 3x3 행렬의 좌표 구하는 dx, dy
dx = [-1, -1, -1, 0, 0, 0, 1, 1, 1]
dy = [-1, 0, 1, -1, 0, 1, -1, 0, 1]


def value_count(input_maps):

    dx_2 = [1, 0, 0, -1]
    dy_2 = [0, -1, 1, 0]

    visited = [[False] * 5 for _ in range(5)]
    total_cnt = 0
    total_group = []

    # 맵 전체 좌표 하나씩 돌면서 BFS 실행
    for j in range(5):
        for k in range(5):
            start_x, start_y = j, k

            # 이미 방문한 좌표라면 패스
            if visited[start_x][start_y]:
                continue
    

            # (start_x, start_y)에서 시작하는 BFS 수행
            visited[start_x][start_y] = True

            q = deque()
            q.append((start_x, start_y, input_maps[start_x][start_y]))
            
            group = [(start_x, start_y)]

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


                for i in range(4):
                    nx, ny = x + dx_2[i], y + dy_2[i]

                    if 0 <= nx < 5 and 0 <= ny < 5 and not visited[nx][ny] and input_maps[nx][ny] == num:
                        
                        q.append((nx, ny, num))
                        group.append((nx, ny))
                        visited[nx][ny] = True
            
            # 찾은 유물 조각이 3개 이상 연결되어 있다면, 맵 안의 전체 유물에 추가
            if len(group) >= 3:

                total_cnt += len(group)
                total_group.extend(group)
            

    return total_cnt, total_group



def spin_arr(degree, input_arr):
    if degree == 0: # 90도 회전
        output_arr = list(map(list, zip(*input_arr[::-1])))
    elif degree == 1: # 180도 회전
        output_arr = [a[::-1] for a in input_arr[::-1]]
    elif degree == 2: # 270도 회전
        output_arr = [x[::-1] for x in list(map(list, zip(*input_arr[::-1])))[::-1]]
    
    return output_arr



def tamsa():

    max_val = -1
    max_group = []
    turned_maps = []

    # 가능한 회전 중심 좌표 : 열이 작은 순서, 행이 작은 순서대로 탐색!
    mid_list = [(1, 1), (2, 1), (3, 1), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)]

    # 각도가 가장 작은 순으로 탐색
    for d in range(3): # d = 0은 90도, d = 1은 180도, d = 2는 270도

        for x, y in mid_list:

            mini_3x3 = []
            
            idx = 0
            for i in range(3):
                line = []
                for j in range(3):
                    nx, ny = x + dx[idx], y + dy[idx]

                    line.append(maps[nx][ny])
                    idx += 1
                mini_3x3.append(line)


            # mini_3x3을 d도 돌리기
            spined_arr = spin_arr(d, mini_3x3)


            # spined_arr를 복사한 맵에 적용하기
            copy_maps = [row[:] for row in maps]

            idx = 0

            for i in range(3):
                for j in range(3):
                    nx, ny = x + dx[idx], y + dy[idx]

                    copy_maps[nx][ny] = spined_arr[i][j]
                    idx += 1


            # 복사한 맵에 spined_arr가 적용된 상태에서 유물 1차 획득 가능한 가치 카운트
            total_cnt, total_group = value_count(copy_maps)

            if total_cnt > max_val:
                max_val = total_cnt
                max_group = total_group
                turned_maps = [row[:] for row in copy_maps]

    return max_val, max_group, turned_maps



# 전체 K번 턴 진행
for turn in range(K):
    
    turn_val = 0

    max_val, max_group, turned_maps = tamsa()


    # 탐사 진행 과정에서 어떠한 유물도 획득 불가였다면 그 즉시 종료
    if max_val <= 0:
        break
    
    # 해당 턴의 유물 가치 누적  
    turn_val += max_val


    # 3x3 회전된 맵을 현재 맵에 적용
    maps = [row[:] for row in turned_maps]

    # 조각이 사라진 위치에 벽면 숫자를 순서대로 채워주기

    max_group.sort(lambda x: [x[1], -x[0]]) # 1) 열 작은순, 2) 행 큰순으로 정렬하기!!!!!


    for ax, ay in max_group:

        maps[ax][ay] = wall[wall_idx]
        wall_idx += 1


    # 유물 연쇄 획득

    total_cnt, total_group = value_count(maps)
    
    # 더 이상 조각이 3개 이상 연결되지 않아 유물이 될 수 없을 때까지 반복
    while total_cnt >= 3:

        # 해당 턴의 유물 가치 누적
        turn_val += total_cnt

        # 조각이 사라진 위치에 벽면 숫자를 순서대로 채워주기
        total_group.sort(lambda x: [x[1], -x[0]]) # 1) 열 작은순, 2) 행 큰순으로 정렬하기

        for ax, ay in total_group:
            maps[ax][ay] = wall[wall_idx]
            wall_idx += 1
        
        # 채운 후 다시 유물 카운트
        total_cnt, total_group = value_count(maps)
            
	
    # 이번 턴의 총 획득 유물 가치 출력
    print(turn_val, end=" ")

0개의 댓글