시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
---|---|---|---|---|---|
2 초 | 512 MB | 93041 | 23753 | 15891 | 24.266% |
수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 0초 후에 2*X의 위치로 이동하게 된다.
수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오.
첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.
수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.
import sys
from collections import deque
RANGE_END = 100_000
start, destination = map(int, sys.stdin.readline().split())
visited = [False for _ in range(RANGE_END + 1)]
queue = deque([(start, 0)])
def get_next_positions(position, moves):
result = []
if position * 2 <= RANGE_END:
result.append((position*2, moves))
if position != 0:
result.append((position-1, moves+1))
if position != RANGE_END:
result.append((position+1, moves+1))
return result
while queue:
pos, moves = queue.popleft()
if pos == destination:
print(moves)
break
visited[pos] = True
for next_pos, moves in get_next_positions(pos, moves):
if not visited[next_pos]:
visited[next_pos] = True
queue.append((next_pos, moves))
아래와 같이 BFS를 이용한다면 풀 수 있을 것이라고 생각했다.
import sys
from collections import deque
RANGE_END = 100_000
start, destination = map(int, sys.stdin.readline().split())
visited = [False for _ in range(RANGE_END + 1)]
queue = deque([(start, 0)])
result = 0
def get_next_positions(position, moves):
result = [(position-1, moves+1), (position+1, moves+1)]
i = 2
while position * i <= RANGE_END:
result.append((position*i, moves))
i *= 2
return result
while queue:
pos, moves = queue.popleft()
if pos == destination:
result = moves
break
visited[pos] = True
for next_pos, moves in filter(lambda x: 0 <= x[0] <= RANGE_END, get_next_positions(pos, moves)):
if not visited[next_pos]:
visited[next_pos] = True
queue.append((next_pos, moves))
print(result)
하지만 위 풀이는 시간 초과 판정을 받았다.
get_next_positions()
함수 내의 while문이 그 범인이었다. i가 100,000이 될 때까지 2를 계속 곱해주면 되니, 그 수가 얼마 되지 않을 것이라고 생각했지만, 실제로 코드를 돌려 보니 1 100000
과 같은 입력들에서 아주 긴 시간이 소요되었다.
그리고 이 문제를 해결하려고 여러 글들을 참고하다가 알게 된 사실들이 있는데, 이것들을 정리해보려고 한다.
https://www.acmicpc.net/board/view/38887#comment-69010
큰 도움이 되었던 글 링크이다.
이 글의 내용에 따르면, 이 문제를 풀기 위해서는 단순한 BFS만으로는 해결할 수 없고,
위와 같은 방법을 사용해야 한다고 한다. 단순한 BFS의 경우에는 모든 간선의 가중치가 동일해야 한다는 전제 조건이 필요하기 때문이다.
나는 BFS에 이런 전제조건이 있는지 몰랐고, queue에 (pos, moves)
와 같이 이동 정보를 포함해주기만 하면 될 줄 알았다. 하지만 이동 정보를 포함해주는 것만으로는 문제가 해결되지 않았다.
다익스트라를 이용한 문제 풀이는 어느 정도 이해가 갈 것 같았으나, 0-1 BFS의 개념은 잘 이해가 가지 않았다. 처음 듣는 개념이었기 때문이다.
정리하자면, 만약 0인 edge를 만난다면 그 edge를 탐으로써 값이 1인 edge를 타는 것보다 더 적은 경로 합을 가질 수 있기 때문에, 그 edge를 최우선으로 처리해야 한다.
이를 위해, 0인 edge를 가지는 노드를 만난다면 queue의 맨 처음에 push(append left)해줘야 한다.
아래 링크에서 그림으로 설명해주고 있다.
https://medium.com/codestories/a-visual-guide-to-0-1-bfs-6f71b64d9a98
다음에 이런 문제를 만났을 때에는, 간선의 가중치가 0이 된다는 사실에 주의하여 다익스트라나 0-1 BFS 알고리즘을 쓰도록 해야겠다.