백준 1473 미로탈출 파이썬
- CS/BOJ
- 2022. 2. 24.
일단 스위치를 켜고 끄면 비트마스킹을 떠올리는 것이 가장 빠르다. 비트마스킹이 가능한지를 먼저 검토해봐야한다.
- 입력으로 들어올 수 있는 최대 크기는 49다.
- 각 행, 열은 총 7개씩 들어올 수 있다. 행, 열의 비트 상태를 나타낼 수 있는 배열은 각각 128(2^7)개다. 각 행마다 각 열의 비트 상태가 나열될 수 있으니 128 * 128이된다.
- 총 만들어지는 열은 128 * 128 * 49 * 2(2 = 방문 처리, 실제 맵 상태 2가지 )160만개다.
메모리 계산을 해보면 6MB 정도로 확인된다. 160만개를 V+E로 방문하게 되면 큰 무리 없이 된다. 따라서 비트마스크 + BFS로 처리할 수 있다.
개인적으로 가장 어려웠던 부분은 비트마스킹 맵을 만드는 것이었다. 처음에는 멍청한 생각으로 각 셀마다 4개의 상태가 있으니 그렇게 만들어볼까? 했는데... 기껏 만들어놓고 보니 이렇게 하면 모든 경우의 수를 체크할 수 없다는 것을 알 수 있게 되었다. 따라서 새로 생각해서 위와 같이 풀었다.
정리하면...
- 비트마스킹을 위해 n,m에 대해 비트마스킹을 처리할 수 있는 방문배열 + 실제 맵상태 배열을 만든다.
- 실제 맵 상태 배열을 만든 후, 어떤 비트가 켜져있는지를 확인해서 실제로 그 맵을 Rotate 시켜주는 역할을 한다.
- 필요한 맵을 만든 후 BFS를 돌린다.
import sys
from collections import deque
# Solution 함수
def bfs(v, my_map) :
# 초기화
que = deque()
que.append((0,0,0,0,0))
#방문 처리
v[0][0][0][0] = 0
while que :
#rbit : 현재 row bit
#cbit : 현재 column bit
#time : 현재 방문 시간
r, c, rbit, cbit, time = que.popleft()
next_time = time + 1
#이 자리에서 스위치를 껐을 때, 비트 상태 전환 (XOR 비트 연산 처리)
next_rbit = rbit ^ (1 << r)
next_cbit = cbit ^ (1 << c)
#도착했으면 종료
if r == n-1 and c == m-1 :
print(time)
return
# 이 자리에서 스위치를 켤 때
if v[next_rbit][next_cbit][r][c] > next_time :
v[next_rbit][next_cbit][r][c] = next_time
que.append((r,c, next_rbit, next_cbit, next_time))
# 좌우 이동 확인
for rr, cc in tra_list_lr:
next_r, next_c = r + rr, c + cc
if -1 < next_r < n and -1 < next_c < m :
# Validation
if ((my_map[rbit][cbit][r][c] == "D" or my_map[rbit][cbit][r][c] == "A") and
(my_map[rbit][cbit][next_r][next_c] == "D" or my_map[rbit][cbit][next_r][next_c] == "A")
and v[rbit][cbit][next_r][next_c] > next_time) :
que.append((next_r, next_c, rbit, cbit, next_time))
v[rbit][cbit][next_r][next_c] = next_time
# 위아래 이동 확인
for rr, cc in tra_list_ud:
next_r, next_c = r + rr, c + cc
if -1 < next_r < n and -1 < next_c < m :
# Validation
if ((my_map[rbit][cbit][r][c] == "C" or my_map[rbit][cbit][r][c] == "A") and
(my_map[rbit][cbit][next_r][next_c] == "C" or my_map[rbit][cbit][next_r][next_c] == "A")
and v[rbit][cbit][next_r][next_c] > next_time):
que.append((next_r, next_c, rbit, cbit, next_time))
v[rbit][cbit][next_r][next_c] = next_time
# 만약에 이동 못할 때, 그만두기.
print(-1)
# 그래프 회전 함수
def rotate_Row(my_list, rbit_list):
global n, m
for r in rbit_list:
for c in range(m):
if my_list[r][c] == "C":
my_list[r][c] = "D"
elif my_list[r][c] == "D":
my_list[r][c] = "C"
def rotate_Col(my_list, cbit_list):
global n, m
for c in cbit_list:
for r in range(n):
if my_list[r][c] == "C":
my_list[r][c] = "D"
elif my_list[r][c] == "D":
my_list[r][c] = "C"
#그래프 복사용 함수
def copy_array(my_list) :
global n,m
return_list = [[] for _ in range(n)]
for idx,value in enumerate(my_list) :
for k in value :
return_list[idx].append(k)
return return_list
# 좌우 이동 그래프
tra_list_lr = [[0, 1], [0, -1]]
# 상하 이동 그래프
tra_list_ud = [[1, 0], [-1, 0]]
# 값 입력
n,m = map(int,sys.stdin.readline().split())
my_list = [[] for _ in range(n)]
for i in range(n):
temp = sys.stdin.readline().rstrip()
for k in temp :
my_list[i].append(k)
#비트 샹태에 따른 방문 배열 : [Row 비트 상태][Col 비트 상태][Row][Col]
v_map = [[[[1000 for _ in range(m)] for _ in range(n)] for _ in range(2**m)] for _ in range(2**n)]
#비트 샹태에 따른 실제 맵 상태 : [Row 비트 상태][Col 비트 상태][Row][Col]
my_map = [[copy_array(my_list) for _ in range(2**m)] for _ in range(2**n)]
# my_map에서 비트 상태에 따라 어떤 맵을 가지고 있는지 만들어준다.
for rbit in range(2**n):
rbit_list = []
# Row에 돌려야할 비트 있는지 확인
for i in range(n):
if rbit & (1 << i) != 0 :
rbit_list.append(i)
for cbit in range(2**m) :
cbit_list = []
# Col에 돌려야할 비트 있는지 확인
for k in range(m) :
if cbit & (1 << k) != 0 :
cbit_list.append(k)
# 비트 정보 넘겨서 Rotate
rotate_Row(my_map[rbit][cbit], rbit_list)
rotate_Col(my_map[rbit][cbit], cbit_list)
#문제 풀이
bfs(v_map, my_map)
'CS > BOJ' 카테고리의 다른 글
백준 14868 문명 파이썬 (0) | 2022.02.25 |
---|---|
백준 1300 말이 되고픈 원숭이 파이썬 (0) | 2022.02.24 |
백준 3197 백조의 호수 파이썬 (0) | 2022.02.24 |
백준 6593 상범빌딩 파이썬 (0) | 2022.02.24 |
백준 텀 프로젝트 9466 파이썬 (0) | 2022.02.22 |