(Python) 백준 13549

Lee Yechan·2024년 1월 8일
0

알고리즘 문제 풀이

목록 보기
35/60
post-thumbnail

백준 13549

시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초512 MB93041237531589124.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만으로는 해결할 수 없고,

  • 다익스트라
  • 0-1 BFS
  • +1이나 -1에 의한 좌표를 큐에 넣을 때 그 좌표의 2의 거듭제곱 배인 좌표들을 전부 큐에 넣음

위와 같은 방법을 사용해야 한다고 한다. 단순한 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 알고리즘을 쓰도록 해야겠다.

profile
이예찬

0개의 댓글