Study/BAEKJOON
[BAEKJOON] 13460번. 구슬 탈출 2
줴림
2025. 4. 9. 16:56
'''
조건:
1. 빨간 구슬과 파란 구슬은 동시에 같은 자리에 있을 수 없음
2. 빨간 구슬이 구멍에 => 성공 / 파란 구슬이 구멍에 => 실패
3. 최대 10번까지 기울일 수 있음 (그 이상은 -1 출력)
4. N: 보드의 세로 크기(행), M: 보드의 가로 크기(열)
5. '.': 빈칸, '#': 벽, 'R': 빨간 구슬, 'B': 파란 구슬, 'O': 구멍
'''
def move(x, y, dx, dy):
count = 0 # 구슬이 몇 칸 움직였나?
# 다음 위치가 벽이 아니고 + 현재 위치가 구멍이 아닌 경우에만
while board[x+dx][y+dy] != '#' and board[x][y] != 'O':
x += dx
y += dy
count += 1
return x, y, count
def dfs(rx, ry, bx, by, depth): # depth: 기울임 횟수
global answer
# 기울임 횟수가 10을 초과하거나 최솟값보다 크면 X
if depth > 10 or depth >= answer:
return
for i in range(4): # 상하좌우 네 방향
nrx, nry, r_count = move(rx, ry, dx[i], dy[i])
nbx, nby, b_count = move(bx, by, dx[i], dy[i])
# 파란 구슬이 구멍에 있으면 실패
if board[nbx][nby] == 'O':
continue
# 빨간 구슬이 구멍에 있으면 성공
if board[nrx][nry] == 'O':
if answer > depth:
answer = depth
return
# 파란 구슬 위치 = 빨간 구슬 위치
# => 더 먼저 온 구슬에게 양보 (더 많이 이동한 구슬 위치 한 칸씩 빼주기)
if (nrx == nbx) and (nry == nby):
if r_count > b_count: # 빨간 구슬이 더 많이 이동
nrx -= dx[i]
nry -= dy[i]
else: # 파란 구슬이 더 많이 이동
nbx -= dx[i]
nby -= dy[i]
# 방문하지 않은 좌표라면
if (nrx, nry, nbx, nby) not in visited:
visited.append((nrx, nry, nbx, nby))
dfs(nrx, nry, nbx, nby, depth+1)
visited.pop()
N, M = map(int, input().split())
board = [list(map(str, input().strip())) for _ in range(N)]
dx = [-1, 0, 1, 0] # ↑, ←, ↓, →
dy = [0, -1, 0, 1] # ↑, ←, ↓, →
# 빨간 구슬, 파란 구슬 위치 찾기
for i in range(N):
for j in range(M):
if board[i][j] == 'R': # 빨간 구슬
rx, ry = i, j
elif board[i][j] == 'B': # 파란 구슬
bx, by = i, j
visited = [] # 방문 좌표 기록용
answer = float('inf') # 최소 기울임 횟수
dfs(rx, ry, bx, by, 1)
answer = answer if answer != float('inf') else -1
print(answer)
다들 bfs로 풀었는데 난 bfs 너무 어려워서 그냥 dfs로 풀었다.
set() 자료형도 익숙치 않아서 visited = []로 했는데, set은 검색/삽입이 O(1)이라 더 효율적이라고 한다.
리스트에서 set으로 바꿔서 제출해봤는데 시간 4ms 단축돼서 지금 당장 이 문제에서는 별 차이가 없는 것 같기도.
+) 아기 상어 문제에서 BFS 공부한 김에 DFS 코드를 직접 BFS로 바꿔봤다.
## BFS 코드 ##
from collections import deque
def move(x, y, dx, dy):
count = 0
# 다음 좌표가 벽이 아니고, 현재 위치가 구멍이 아님
while area[x+dx][y+dy] != '#' and area[x][y] != 'O':
x += dx
y += dy
count += 1
return x, y, count
def bfs(rx, ry, bx, by):
visited_r = [[False] * M for _ in range(N)]
visited_b = [[False] * M for _ in range(N)]
q = deque()
q.append((rx, ry, bx, by, 1))
visited_r[rx][ry] = True
visited_b[bx][by] = True
while q:
rx, ry, bx, by, depth = q.popleft()
# 기울임 횟수가 10을 넘어가면 -1
if depth > 10:
return -1
for i in range(4):
nrx, nry, r_count = move(rx, ry, dx[i], dy[i])
nbx, nby, b_count = move(bx, by, dx[i], dy[i])
# 파란 구슬이 구멍에 도착 => 실패
if area[nbx][nby] == 'O':
continue
# 빨간 구슬이 구멍에 도착 => 성공
if area[nrx][nry] == 'O':
return depth
# 파란 구슬 위치 == 빨간 구슬 위치
## 더 많이 이동한 구슬이 양보한다.
if (nbx == nrx) and (nby == nry):
if r_count > b_count:
nrx -= dx[i]
nry -= dy[i]
else:
nbx -= dx[i]
nby -= dy[i]
# 방문하지 않은 위치
if not visited_r[nrx][nry] and not visited_b[nbx][nby]:
visited_r[nrx][nry] = True
visited_b[nbx][nby] = True
q.append((nrx, nry, nbx, nby, depth+1))
return -1
N, M = map(int, input().split()) # N: 행, M: 열
area = [list(map(str, input().strip())) for _ in range(N)]
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
# 빨간 구슬, 파란 구슬 위치 찾기
for i in range(N):
for j in range(M):
if area[i][j] == 'R':
rx, ry = i, j
area[i][j] = '.'
elif area[i][j] == 'B':
bx, by = i, j
area[i][j] = '.'
answer = bfs(rx, ry, bx, by)
print(answer)