: 알고리즘 잡스에서 푼 문제중에 역대급.. 이거 결국 혼자 힘으로는 60점 밖에 못냈고 3일에 걸쳐서 풀었다.. 이렇게 풀면 안되긴 하는데 이 고비를 스스로 넘겨야 할 것 같았다..BFS ^^
: 특정 좌표에서 로봇에게 명령을 내려(1,2번) 특정 좌표로 이동하게 할 때 최소 몇번의 명령을 내려서 갈 수 있는지를 구하는 문제이다. 이 때, 명령1은 k만큼 앞으로 전진하도록 하는 것인데, 주어진 궤도에서만 전진이 가능하다. 즉, 0인 칸에서만 전진이 가능하고, 1로는 갈 수 없다. 그리고 k는 1에서 3 사이로 최대 3칸까지 전진이 가능하다(한번의 명령으로). 명령2는 방향을 바꾸는 명령인데 현재 방향을 기준으로 오른쪽, 왼쪽으로만 방향을 바꿀 수 있다. 예를 들어, 동쪽을 바라보고 있는 상태라면 북쪽과 남쪽으로만 방향을 바꿀 수 있다. 입력은 로봇이 움직일 궤도 정보와 그 궤도 정보가 NxM 행렬일 때, N,M의 정보, 그리고 로봇의 시작 좌표 및 시작 방향과 도착 좌표 및 도착 방향을 준다. 이를 바탕으로 최소 명령수를 리턴하면 되는 문제이다.
: 사실 헤맸던 포인트라는 워딩(?)이 무색할 정도로 이 문제를 어떻게 접근할지 갈피조차 잡지 못했다. 처음에 평소 하던대로 탐색 공간을 알아봤고, 최소 거리를 구하는 문제이기에 BFS를 떠올렸다. 하지만, 내가 여태까지 풀었던 BFS 문제들은 대부분이 탐색하면서 하나의 값만 저장하면서 탐색을 했었는데, 이번에는 그 조건들이 다양했던 문제여서 머리가 복잡했고, 해결책을 찾기가 쉽지 않았다. 후회되는 포인트는 차라리 빨리 질문을 계속해서 문제를 푸는게 나았던 것 같다. 같은 문제를 계속해서 보니까 계속 같은 생각만하고 무한루프가 돌더라..
어쨌든 헤맸던 포인트를 정리해보면,
다음으로 고칠점을 정리해보면
: 이 문제를 해결하기 위한 포인트는 하나의 위치 및 바라보는 방향을 한개의 정보 단위로 정하고, 그 정보 단위를 하나의 노드로 생각하고, 이를 그래프화해서 BFS를 한다는 생각을 하는 것이다. 나에게는 여전히 복잡한 얘기인데, 천천히 따라가보면, 먼저, BFS라는 것 자체가 그래프라는 자료구조를 바탕으로 이뤄지는 알고리즘이다. 루트 노드를 기준으로 각 높이마다 자식 노드를 한층씩 쭉 훑으면서 가장 아래쪽 층까지를 순차적으로 탐색하는 방식이라고 할 수 있다. 이 때, 루트 노드를 이 문제에서 '방향 정보 + 좌표 정보'라고 했을 때 루트 노드에서 만들 수 있는 혹은 나아갈 수 있는 새로운 정보들을 나열해보면, 예를 들어, 루트 노드가 (5,4,남쪽 방향)이라는 정보라면, 이 정보를 바탕으로 만들 수 있는 혹은 나아갈 수 있는 자식 노드들은 이렇게 된다.
이런식으로 총 5개의 명령어를 새로 입력하여 새로운 좌표값과 방향을 얻을 수 있다. 그리고 이는 모든 노드에서 동일하게 5개의 명령어를 입력할 수 있다. 모든 노드에서 오른쪽, 왼쪽으로 방향 전환을 할 수 있을 것이고(2개), 향하던 방향으로 1,2,3칸씩 움직이도록 명령할 수 있다(3개).
여기서 다시 우리가 최종적으로 구하고자 하는 값을 상기시켜보면 특정 좌표로 가기 위한 최소 명령어의 수였다. 그러면 BFS를 하면서 특정 좌표에 도착할 때까지 쓰인(?) 명령어의 개수를 표기하면서 진행해야하고, 마지막에 도착하고자 하는 좌표에 그 때까지 쓰인 최소 명령어가 적히도록 해야한다. 하지만 이번 문제는 그리 간단하지 않다.. 생각해야할 정보가 한개가 아닌 두개 이상이다. 그래서 여기서는 아예 방향 별로 배열을 따로 만든다음에 풀어줘야한다. 동쪽을 향하는 케이스에서 생기는 정보들은 동쪽용(?) 배열에 입력하고, 서쪽을 향하는 케이스에서의 정보들은 서쪽용 배열에 입력하는 것이다. 이렇게 해야지만, 최종 목적지에 도달한 최소 명령어 수를 구할 수 있는데, 이유는 이렇게 안하면 예를 들어, 도착지를 갈 수 있는 방법이 동쪽을 향해 가다가도 갈 수 있고, 서쪽을 향해 가다가도 갈 수 있는 곳이라면, 둘중에 어떤 곳이 여태까지 명령어를 더욱 조금 입력해왔는지 알 수가 없다. 분명 이전의 방식대로라면 겹치는 부분을 탐색하지 않을 것이기에 방향별로 나눠서 생각해야한다.
import copy
n, m = map(int, input().split())
board = []
for i in range(n):
board.append([1,1,1]+list(map(int, input().split()))+[1,1,1])
limit_line = [[1 for i in range(m+6)],[1 for i in range(m+6)],[1 for i in range(m+6)]]
board = limit_line + board + limit_line
a,b,w1 = map(int, input().split())
c,d,w2 = map(int, input().split())
ways = {}
for i in range(1,5):
copy_board = []
for j in range(n+6):
row = []
for k in range(m+6):
if board[j][k] == 0:
row.append(None)
elif board[j][k] == 1:
row.append("NULL")
copy_board.append(row)
ways[i] = copy_board
queue = [(w1,a+2,b+2)]
ways[w1][a+2][b+2] = 0
dx = [None, 0, 0, 1, -1]
dy = [None, 1, -1, 0, 0]
possible_dir = [None,[3,4],[3,4],[1,2],[1,2]]
while len(queue) != 0:
top = queue.pop(0)
for i in possible_dir[top[0]]:
if ways[i][top[1]][top[2]] is None:
ways[i][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
queue.append((i,top[1],top[2]))
# if top[0] == 1:
# if ways[3][top[1]][top[2]] is None:
# ways[3][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((3,top[1],top[2]))
# if ways[4][top[1]][top[2]] is None:
# ways[4][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((4,top[1],top[2]))
# elif top[0] == 2:
# if ways[3][top[1]][top[2]] is None:
# ways[3][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((3,top[1],top[2]))
# if ways[4][top[1]][top[2]] is None:
# ways[4][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((4,top[1],top[2]))
# elif top[0] == 3:
# if ways[1][top[1]][top[2]] is None:
# ways[1][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((1,top[1],top[2]))
# if ways[2][top[1]][top[2]] is None:
# ways[2][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((2,top[1],top[2]))
# elif top[0] == 4:
# if ways[2][top[1]][top[2]] is None:
# ways[2][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((2,top[1],top[2]))
# if ways[1][top[1]][top[2]] is None:
# ways[1][top[1]][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((1,top[1],top[2]))
# 여기까지 방향끝
# 여기서부터 직진 1<=k<=3
for step in range(1,4):
if ways[top[0]][top[1]+(dx[top[0]]*step)][top[2]+(dy[top[0]]*step)] is None:
ways[top[0]][top[1]+(dx[top[0]]*step)][top[2]+(dy[top[0]]*step)] = ways[top[0]][top[1]][top[2]] + 1
queue.append((top[0],top[1]+(dx[top[0]]*step),top[2]+(dy[top[0]]*step)))
else:
break
# if top[0] == 1:
# prev = False
# if ways[top[0]][top[1]][top[2]+1] is None:
# prev = True
# ways[top[0]][top[1]][top[2]+1] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1],top[2]+1))
# if ways[top[0]][top[1]][top[2]+2] is None and prev:
# prev = True
# ways[top[0]][top[1]][top[2]+2] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1],top[2]+2))
# if ways[top[0]][top[1]][top[2]+3] is None and prev:
# ways[top[0]][top[1]][top[2]+3] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1],top[2]+3))
# elif top[0] == 2:
# prev = False
# if ways[top[0]][top[1]][top[2]-1] is None:
# prev = True
# ways[top[0]][top[1]][top[2]-1] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1],top[2]-1))
# if ways[top[0]][top[1]][top[2]-2] is None and prev:
# prev = True
# ways[top[0]][top[1]][top[2]-2] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1],top[2]-2))
# if ways[top[0]][top[1]][top[2]-3] is None and prev:
# ways[top[0]][top[1]][top[2]-3] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1],top[2]-3))
# elif top[0] == 3:
# prev = False
# if ways[top[0]][top[1]+1][top[2]] is None:
# prev = True
# ways[top[0]][top[1]+1][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1]+1,top[2]))
# if ways[top[0]][top[1]+2][top[2]] is None and prev:
# prev = True
# ways[top[0]][top[1]+2][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1]+2,top[2]))
# if ways[top[0]][top[1]+3][top[2]] is None and prev:
# ways[top[0]][top[1]+3][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1]+3,top[2]))
# elif top[0] == 4:
# prev = False
# if ways[top[0]][top[1]-1][top[2]] is None:
# prev = True
# ways[top[0]][top[1]-1][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1]-1,top[2]))
# if ways[top[0]][top[1]-2][top[2]] is None and prev:
# prev = True
# ways[top[0]][top[1]-2][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1]-2,top[2]))
# if ways[top[0]][top[1]-3][top[2]] is None and prev:
# ways[top[0]][top[1]-3][top[2]] = ways[top[0]][top[1]][top[2]] + 1
# queue.append((top[0],top[1]-3,top[2]))
if isinstance(ways[w2][c+2][d+2],int):
print(ways[w2][c+2][d+2])
break
: 위에는 풀이 코드인데, 중간에 주석 처리한 것은 처음에 너무 하드코딩으로 만들어서 간소화 시킨 부분을 주석처리 해놓은 것이다.
: 이 문제는 두고두고 풀어봐야겠다..
import copy
n,m = map(int, input().split())
board = []
for i in range(3):
board.append(["BLOCK" for k in range(m+6)])
for i in range(n):
input_list = list(map(int, input().split()))
for j in range(m):
for idx,el in enumerate(input_list):
if el == 0:
input_list[idx] = "ROAD"
elif el == 1:
input_list[idx] = "BLOCK"
board.append((["BLOCK"]*3) + input_list + (["BLOCK"]*3))
for i in range(3):
board.append(["BLOCK" for k in range(m+6)])
maps = {}
for way in range(1,5):
copy_board = copy.deepcopy(board)
maps[way] = copy_board
a,b,w1 = map(int,input().split())
c,d,w2 = map(int,input().split())
root = (w1,a+2,b+2)
queue = [root]
maps[root[0]][root[1]][root[2]] = 0
def check_direction(top):
global maps
global queue
possible_dir = [None, [3,4],[3,4],[1,2],[1,2]]
for d in possible_dir[top[0]]:
if maps[d][top[1]][top[2]] == "ROAD":
maps[d][top[1]][top[2]] = maps[top[0]][top[1]][top[2]] + 1
queue.append((d,top[1],top[2]))
def check_forward(top):
global maps
global queue
direction = top[0]
dx = [None, 0, 0, 1, -1]
dy = [None, 1, -1, 0, 0]
for i in range(1,4):
if maps[direction][top[1]+(dx[direction]*i)][top[2]+(dy[direction]*i)] == "ROAD":
maps[direction][top[1]+(dx[direction]*i)][top[2]+(dy[direction]*i)] = maps[direction][top[1]][top[2]] + 1
queue.append((direction, top[1]+(dx[direction]*i), top[2]+(dy[direction]*i)))
else:
break
while len(queue) != 0:
top = queue.pop(0)
check_direction(top)
check_forward(top)
if isinstance(maps[w2][c+2][d+2], int):
print(maps[w2][c+2][d+2])
break
: 함수를 만들어서 메인 로직의 코드를 간소화 시켰다. 이런식으로 해야 에러를 만드는 수가 줄어들 것 같다.