💻 백준 16953: A→B
- 문제 링크: https://www.acmicpc.net/problem/16953
- 알고리즘 분류: 그리디, 너비 우선 탐색(BFS)
문제 소개 🧐
정수 A를 B로 바꾸려고 한다. 가능한 연산은 다음과 같은 두 가지이다.
- 2를 곱한다.
- 1을 수의 가장 오른쪽에 추가한다.
A를 B로 바꾸는데 필요한 연산의 최솟값을 구해보자.
입력 📝
- 첫째 줄에 A, B (1 ≤ A < B ≤ 10^9)가 주어진다.
출력 📤
- A를 B로 바꾸는데 필요한 연산의 최솟값에 1을 더한 값을 출력한다.
- 만들 수 없는 경우에는 -1을 출력한다.
제한 ❌
- 2초, 512MB
첫 번째 시도: BFS (실패) 👊
Idea
A에서 B로 가는 경우의 수를 모두 탐색하기보다, B에서 A로 역으로 연산을 수행하는 것이 더 효율적일 것이라 생각했다. B에 대해 가능한 역연산은 다음과 같다.
- 2로 나눈다.
- 오른쪽의 1을 제거한다.
B에서부터 시작하여 A가 될 때까지 BFS(너비 우선 탐색)를 적용했다. 메모리 초과를 피하기 위해 visited
배열 없이, (현재 값, 연산 횟수)를 튜플로 큐에 저장하여 탐색을 진행했다.
Code
import sys
from collections import deque
a, b = map(int, sys.stdin.readline().split())
q = deque()
q.append((b, 0))
while q:
num, cnt = q.popleft()
if num % 10 == 1:
next_num = num // 10
if next_num == a:
print(cnt + 2)
exit()
q.append((next_num, cnt + 1))
# 짝수 여부 확인 없이 2로 나누는 로직 오류
next_num = num // 2
if next_num == a:
print(cnt + 2)
exit()
q.append((next_num, cnt + 1))
print(-1)
Result
- 실패 ⏰
num
이 홀수일 때도 2로 나누는 연산을 시도하는 로직 오류가 있었다. 또한,q
가 비었을 때 루프가 정상적으로 종료되지 않는 문제가 있었다.
두 번째 시도: BFS (로직 수정) ✨
Idea
첫 번째 시도의 문제점을 수정했다.
- 2로 나누는 연산은
num
이 짝수일 때만 수행하도록if num % 2 == 0:
조건을 추가했다. - 큐(
q
)가 비었을 때while
루프가 정상적으로 종료되고, 최종적으로 -1이 출력되도록 로직을 보완했다. - 시작 연산 횟수를 1로 설정하여 출력 시
cnt
만으로 최종 연산 횟수를 계산하도록 수정했다.
Code
import sys
from collections import deque
a, b = map(int, sys.stdin.readline().split())
q = deque()
q.append((b, 1)) # 시작 연산 횟수를 1로 설정
while q:
num, cnt = q.popleft()
if num == a:
print(cnt)
exit()
if num % 10 == 1:
next_num = num // 10
if next_num >= a:
q.append((next_num, cnt + 1))
if num % 2 == 0:
next_num = num // 2
if next_num >= a:
q.append((next_num, cnt + 1))
print(-1)
Result
- 성공! ✅
- B에서 A로 가는 역연산을 통해 탐색 공간을 줄이고, BFS를 적용하여 최단 경로(최소 연산)를 효율적으로 찾을 수 있었다.
개선할 부분 🤔
- 현재 코드는 BFS를 사용하여 모든 가능한 경우를 탐색하지만, 사실 이 문제는 그리디한 접근으로도 해결할 수 있다.
- B에서 역연산을 할 때, 2로 나누어떨어지면 2로 나누고, 그렇지 않고 끝이 1이면 1을 떼는 방식으로 하나의 경로만 탐색해도 정답을 찾을 수 있다. 이 경우 코드가 더 간결해질 수 있다.