BOJ_13460_구슬 탈출2

깊이 우선 탐색(DFS)를 사용해서 구현하는 문제

10번 이하로 움직일 수 있으며, 빨간 구슬이 들어가도록 하는 최소 움직임 횟수를 구한다
단, 파란 구슬이 들어가거나, 두 구슬이 동시에 들어가서도 안 된다

사방으로 구슬을 보내면서 성공할 시 최솟값을 갱신하면 된다. 구슬은 길이 이어지지 않을 때까지 앞으로 보내면 된다. 동시에 이동할 경우, 방향에 따라 해당 방향 기준 더 앞에 있는 구슬을 먼저 보낸다. 그렇게 하지 않으면 구슬을 움직일 때 오차가 생긴다.

음의 방향으로 이동할 때와 양의 방향으로 이동할 때의 대소 비교가 반대이기 때문에 위치를 비교할 때 dx, dy의 값을 곱해주었다

order = False if b_idx[0] * dx[i] > r_idx[0] * dx[i] else True
order = False if b_idx[1] * dy[i] > r_idx[1] * dy[i] else True

예를 들어 rx = 2, bx = 3일 때 오른쪽 방향 진로는 2 < 3이므로 b를 먼저 보내고 왼쪽 방향 진로는 -2 > -3 이 되어서 r을 먼저 보내게 된다

삽질

구슬 바로 주변에 벽이라면 진행을 갱신하는 의미가 없기 때문에 사방을 한 칸 확인 후 dfs를 실행하지 않았다.

빨간 구슬만 확인하면 된다고 생각해서(왜 그랬을까?) 빨간 구슬이 안 움직이는 걸 조건으로 작성했는데, 그러면 안 되고 두 구슬이 모두 해당 진행 방향으로 갈 수 없을 때를 조건으로 넣어줘야 한다. 왜냐면 파란 구슬이 먼저 들어갈 수도 있는 것이고, 파란 구슬의 위치가 달라져 빨간 구슬이 들어갈 수 있게 되기도 하기 때문이다.

# 파란 구슬 구멍에 들어가면 안 됨  
# 기울여서 구슬로 빼내기  
# 더 이상 구슬이 움직이지 않을 때 -> 끝에 걸릴 때까지 기울여둔다  
#  
# 최소 몇 번만에? 못 하면 -1#  
# dfs로 사방으로 보내고, 최솟값 갱신  
import copy  
import sys  
  
dx = [-1, 1, 0, 0]  
dy = [0, 0, -1, 1]  
  
# 입력  
N, M = list(map(int, input().split()))  
board_origin = []  
b_start = ()  
r_start = ()  
min_cnt = sys.maxsize  
  
for i in range(N):  
    board_origin.append(list(input()))  
  
    if 'B' in board_origin[i]:  
        b_start = (i, board_origin[i].index('B'))  
  
    if 'R' in board_origin[i]:  
        r_start = (i, board_origin[i].index('R'))  
  
  
def four_dir(b_idx, r_idx, cnt, board):  
    global min_cnt  
    if cnt > 10 or cnt >= min_cnt:  
        return  
  
    for i in range(4):  
        # 바로 막혀있다면 진입 안 함  
        if board[r_idx[0] + dx[i]][r_idx[1] + dy[i]] == '#' and board[b_idx[0] + dx[i]][b_idx[1] + dy[i]] == '#':  
            continue  
  
        # 바로 막혀있진 않다면 진입  
        if i <= 1:  
            order = False if b_idx[0] * dx[i] > r_idx[0] * dx[i] else True  
  
        else:  
            order = False if b_idx[1] * dy[i] > r_idx[1] * dy[i] else True  
  
        dfs(b_idx, r_idx, order, i, cnt + 1, copy.deepcopy(board))  
  
  
# order : B가 앞이면 False, R이 앞이면 Truedef dfs(b_idx, r_idx, order, direction, cnt, board):  
    global min_cnt  
  
    if not order:  
        b_idx, b_flag = go_straight(b_idx, direction, board, 'B')  
        r_idx, r_flag = go_straight(r_idx, direction, board, 'R')  
  
    else:  
        r_idx, r_flag = go_straight(r_idx, direction, board, 'R')  
        b_idx, b_flag = go_straight(b_idx, direction, board, 'B')  
  
    # 실패  
    if b_flag:  
        return  
  
    # 성공  
    elif r_flag:  
        min_cnt = min(cnt, min_cnt)  
        return  
  
    four_dir(b_idx, r_idx, cnt, board)  
  
  
def go_straight(idx, direction, board, c):  
    x = idx[0]  
    y = idx[1]  
  
    while True:  
        x += dx[direction]  
        y += dy[direction]  
  
        if not (board[x][y] == 'O' or board[x][y] == '.'):  
            board[idx[0]][idx[1]] = '.'  
            board[x - dx[direction]][y - dy[direction]] = c  
  
            return (x - dx[direction], y - dy[direction]), False  
  
        elif board[x][y] == 'O':  
            board[idx[0]][idx[1]] = '.'  
            board[x][y] = 'O'  
            return idx, True  
  
  
four_dir(b_start, r_start, 0, board_origin)  
print(min_cnt if min_cnt < sys.maxsize else -1)