ABC 339 D번 복기 - 4차원 visited 배열 BFS 백트래킹
1. 문제
D - Synchronized Players (atcoder.jp)
D - Synchronized Players
AtCoder is a programming contest site for anyone from beginners to experts. We hold weekly programming contests online.
atcoder.jp
2. 풀이
하라는대로 구현하면 된다
두 플레이어 P가 움직이는데 두 플레이어는 같은 방향으로 움직이게 되어있다.
또한 한 플레이어가 해당 방향으로 움직일수 없더라도 다른 플레이어가 움직일 수 있다면 움직이는 경우로 생각한다
그래서 먼저 두 플레이어의 위치를 찾고 BFS를 수행하는 전형적인 형태
n = int(stdin.readline())
maps = [list(stdin.readline().rstrip()) for _ in range(n)]
player = []
i = 1
for y in range(n):
for x in range(n):
if maps[y][x] == 'P':
player.append((x,y,i))
i += 1
print(bfs(player,maps,n))
BFS를 구현할 때 이제 생각을 조금 해야함
결국에 visited 처리를 해줘야 시간초과가 안날것인데 visited 처리를 어떻게 해줘야할것인가?
1) (x,y)와 (z,w)가 움직이는 형태이므로 visited[x][y][z][w]의 4차원 배열을 생각하면 될 것 같다.
이 때 map의 크기가 최대 60이니까 60*60*60*60 = 12960000정도로 1000만 정도면 선언이 가능할 것 같다
visited = [[[[0 for _ in range(n)] for _ in range(n)] for _ in range(n)] for _ in range(n)]
한 플레이어가 움직이지 못하더라도, 다른 플레이어가 움직일 수 있는데 그런 경우에도 움직이는 것으로 친다.
2) 그러면 한 플레이어의 이동 후 좌표 범위가 map을 벗어났거나, 이동했을때 장애물이 있다면, 이동하기 전 좌표로 되돌린다
for a,b in [[0,1],[1,0],[0,-1],[-1,0]]:
dx = x + a
dy = y + b
dz = z + a
dw = w + b
if dx < 0 or dx > n-1 or dy < 0 or dy > n-1 or maps[dy][dx] == '#':
dx = x
dy = y
if dz < 0 or dz > n-1 or dw < 0 or dw > n-1 or maps[dw][dz] == '#':
dz = z
dw = w
최소로 이동하는 횟수를 구해야하기 때문에 각 경우에 대해서 count를 세줘야한다.
이동하고 나서 visited[dx][dy][dz][dw]가 0이라면 아직 방문하지 않은 곳이니까..
count + 1을 해줘야하는데
기본적인 실수를 해버렸단말이지
if visited[dx][dy][dz][dw] == 0:
visited[dx][dy][dz][dw] = 1
count += 1
player = [(dx,dy,1),(dz,dw,2)]
queue.append((player,count))
이렇게 해버린다면..?
(x,y) (z,w)에서 count일때
위로 가는 경우와 아래로 가는 경우, 오른쪽으로 가는 경우, 왼쪽으로 가는 경우 모두 count+1이 들어가야하는데
그냥 count+=1을 해버리면..
위로 가는 경우 count+1, 아래로 가는 경우 count+2 오른쪽으로 가는 경우 count+3, 왼쪽으로 가는 경우 count+4가 들어가잖아.
그래서 dcount = count+1로 해서.. 구별해줘야겠지
많이하던건데 실수했으니까 다시한번 짚고
if visited[dx][dy][dz][dw] == 0:
visited[dx][dy][dz][dw] = 1
dcount = count + 1
if dx == dz and dy == dw:
if answer > dcount:
answer = dcount
else:
player = [(dx,dy,1),(dz,dw,2)]
queue.append((player,dcount))
dx == dz, dy == dw이면 두 플레이어가 동일한 위치에 온 경우니까 count의 최솟값을 갱신해준다.
그리고나서는 queue에 넣지 말아야겠지
여기서 visited를 하지 말아야하냐 해야하냐 고민했는데..
더 최솟값으로 동일한 경우에 올수도 있으니까?
근데 그랬다면 더 빨리 갱신되었어야하니까 visited를 하는게 맞아
from collections import deque
from sys import stdin
def bfs(player,maps,n):
visited = [[[[0 for _ in range(n)] for _ in range(n)] for _ in range(n)] for _ in range(n)]
count = 0
queue = deque()
queue.append((player,count))
INF = 1000000000000000000000000000000000
answer = INF
while queue:
player,count = queue.popleft()
x = 0
y = 0
z = 0
w = 0
for a,b,i in player:
if i == 1:
x = a
y = b
else:
z = a
w = b
for a,b in [[0,1],[1,0],[0,-1],[-1,0]]:
dx = x + a
dy = y + b
dz = z + a
dw = w + b
if dx < 0 or dx > n-1 or dy < 0 or dy > n-1 or maps[dy][dx] == '#':
dx = x
dy = y
if dz < 0 or dz > n-1 or dw < 0 or dw > n-1 or maps[dw][dz] == '#':
dz = z
dw = w
if visited[dx][dy][dz][dw] == 0:
visited[dx][dy][dz][dw] = 1
dcount = count + 1
if dx == dz and dy == dw:
if answer > dcount:
answer = dcount
else:
player = [(dx,dy,1),(dz,dw,2)]
queue.append((player,dcount))
if answer == INF:
answer = -1
return answer
근데 이렇게하면 시간초과나더라
마지막으로 생각해볼 테크닉은 백트래킹이다.
행동횟수의 최솟값을 구하기 때문에.. 현재 행동횟수 count가 이미 갱신된 answer보다 크다면...
당연히 더 이상 볼필요도 없으니까 continue로 넘어가는 기본적인 백트래킹 테크닉을 적용
while queue:
player,count = queue.popleft()
if count > answer:
continue
x = 0
y = 0
z = 0
w = 0
for a,b,i in player:
if i == 1:
x = a
y = b
else:
z = a
w = b
'경쟁 프로그래밍 > Atcoder' 카테고리의 다른 글
ABC 348 D번 복기 - 그래프를 재구성하고 BFS/DFS를 해야하는 문제 (0) | 2024.04.10 |
---|---|
ABC 340 F번 복기 - 확장 유클리드 알고리즘 제대로 활용하기 (0) | 2024.02.21 |
ABC 336 C번 복기 - 0,2,4,6,8로만 만든 숫자들 중 n번째 숫자를 찾는 놀라운 방법 (0) | 2024.01.21 |
ABC 337 D번 복기 - 2차원 배열에서 행,열로 연속한 O의 개수를 빠르게 세는 방법(sliding window + prefix sum) (0) | 2024.01.21 |
ABC 335 C번 복기 - 놓치기 쉬운 deque를 사용할 때 주의할 점(deque indexing is O(N)) + 배열과 연결리스트 접근 시간복잡도 차이 (0) | 2024.01.07 |