개발자입니다
[백준] 13913번 숨바꼭질 4 본문
숨바꼭질 4
시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
2 초 | 512 MB | 55239 | 18696 | 13051 | 31.167% |
문제
수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.
수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오.
입력
첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.
출력
첫째 줄에 수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.
둘째 줄에 어떻게 이동해야 하는지 공백으로 구분해 출력한다.
예제 입력 1
5 17
예제 출력 1
4
5 10 9 18 17
예제 입력 2
5 17
예제 출력 2
4
5 4 8 16 17
내 코드
메모리 초과
from collections import deque
def bfs(N, K):
if N == K:
'''일치하면 반환'''
return 0, [N]
elif N > K:
'''N > K 면 빼기해서 반환'''
return N - K, list(range(N, K - 1, -1))
else:
'''N < K 면'''
MAX = 100001
visited = [False] * MAX
positions = [[] for _ in range(MAX)]
queue = deque([N])
visited[N] = True # 첫 위치 방문처리
positions[N].append(N)
while queue:
pos = queue.popleft()
for next_pos in (2 * pos, pos + 1, pos - 1):
if 0 <= next_pos <= 100000 and not visited[next_pos]:
visited[next_pos] = True
queue.append(next_pos)
positions[next_pos].extend([*positions[pos], next_pos]) # [5, 10]...
if next_pos == K:
return len(positions[next_pos]) - 1, positions[next_pos]
N, K = map(int, input().split())
moves, positions = bfs(N, K)
print(moves)
print(*positions)
chatGPT
우선, 문제를 해결하기 위해 BFS를 사용해야 한다는 것은 적절한 접근법입니다. 하지만 제시된 코드의 메모리 초과 문제는 다음과 같은 원인 때문입니다:
문제점 분석
- positions 리스트 사용 방식
- positions[next_pos].extend([*positions[pos], next_pos]) 부분에서, 경로를 계속 리스트에 저장하면서 메모리를 많이 소비합니다.
- 0부터 100000까지의 모든 위치에 대해 경로를 저장하므로 공간 복잡도가 O(N^2) 수준이 될 수 있음.
- visited 배열이 True/False만 저장
- visited를 단순히 방문 여부만 체크하는 것이 아니라, 이전 위치(parent) 정보를 저장하는 방식으로 개선할 수 있음.
개선된 접근법
- BFS에서 부모 정보 저장 (parent 배열 활용)
- visited 대신 parent 배열을 사용하여 경로를 역추적하는 방식으로 변경.
- parent= prev_x 형태로, x에 도달하기 직전의 위치를 저장.
- 경로 역추적
- K에서 출발하여 parent를 거슬러 올라가면서 최단 경로를 복원.
- 공간 복잡도 개선
- 원래 코드처럼 모든 경로를 positions에 저장하는 방식 대신, O(N) 공간만 사용.
- 시간 복잡도는 여전히 O(N).
개선된 코드
from collections import deque
def bfs(N, K):
if N == K:
return 0, [N]
elif N > K:
return N - K, list(range(N, K - 1, -1))
MAX = 100001
parent = [-1] * MAX # 각 위치의 부모 저장
queue = deque([N])
parent[N] = N # 자기 자신을 부모로 설정
while queue:
pos = queue.popleft()
for next_pos in (pos - 1, pos + 1, 2 * pos):
if 0 <= next_pos < MAX and parent[next_pos] == -1:
parent[next_pos] = pos # 부모 위치 저장
queue.append(next_pos)
if next_pos == K:
path = []
while next_pos != N: # 역추적하여 경로 복원
path.append(next_pos)
next_pos = parent[next_pos]
path.append(N)
path.reverse()
return len(path) - 1, path # 이동 횟수, 경로 반환
N, K = map(int, input().split())
moves, positions = bfs(N, K)
print(moves)
print(*positions)
개선된 코드의 장점
- 메모리 절약 (O(N))
- positions 배열을 유지하지 않고, parent 배열로 경로를 역추적함.
- 모든 위치에 대해 리스트를 유지하는 대신, parent 배열만 저장.
- 시간 복잡도 유지 (O(N))
- BFS를 사용하므로 여전히 최적의 성능.
- 출력 시 불필요한 리스트 복사 제거
- parent를 활용하여 while 루프에서 거꾸로 경로를 추적한 후 reverse() 사용.
부가 질문
내 생각
- 부모 값을 저장하는 것을 생각하지 못했다.
'코딩테스트 > Python 문제풀기' 카테고리의 다른 글
[백준] 17086번 아기 상어 2 (0) | 2025.02.04 |
---|---|
[백준] 14226번 이모티콘 (0) | 2025.02.02 |
[백준] 13549번 숨바꼭질3 (0) | 2024.11.23 |
[백준] 12851번 숨바꼭질 2 (0) | 2024.11.20 |
[백준] 16953번 A → B (1) | 2024.11.19 |