백준 1463 — 1로 만들기: 최소 연산 횟수 구하기

1을 빼거나 2·3으로 나누는 선택을 비교해 1까지의 최소 연산 횟수를 구한다.

  • 문제 풀이
  • DP

백준 1463 문제 보기

문제

백준 1463 1로 만들기 문제 화면

1. 테이블 정의하기

D[i] = i를 1로 만들기 위해 필요한 연산 사용 횟수의 최솟값

2. 점화식 찾기

  • 예를 기반으로 분석 예시 D[12]를 어떻게 구할 수 있을까? 12가 3으로 나눠지나? → O : D[4] + 1 일 수 있다. 12가 2로 나눠지나? → O D[6] + 1 일 수 있다. 12가 1보다 크나? → D[11] + 1일 수 있다. 셋 중 가장 작은 거를 D[12]에 적으면 되지 않을까?
Text
D[k] = ?
3으로 나누어지면 3으로 나누거나 (D[k] = D[k/3] + 1)
2로 나누어지면 2로 나누거나 (D[k] = D[k/2] + 1)
1을 빼거나 (D[k] = D[k-1] + 1), 이들 중에서 최솟값이 D[k]가 된다.

3. 초기값 정의하기

Text
D[1] = 0 -> 이것만 정의해줘도 점화식이 도는데에는 문제가 없다. (D[k-1]만 알면되니까)
 
D[2] = 1 (/2)
 
D[3] = 1 (/3)

4. 코드 구현하기

JavaScript
const fs = require('fs');
const filePath = process.platform === 'linux' ? '/dev/stdin' : './input.txt';
 
const N = Number(fs.readFileSync(filePath, 'utf-8'));
 
const DP = Array.from({ length: 1000000 + 1 }).fill(0);
 
// 초기값 정의
DP[1] = 0;
DP[2] = DP[3] = 1; // 이 코드는 반복문을 4부터 시작하므로 필요하다.
 
// 점화식을 포함하는 for문 작성
for (let i = 4; i <= N; i++) {
    let min = Infinity;
 
    if (i % 2 === 0) {
        min = Math.min(min, DP[i / 2] + 1);
    }
    if (i % 3 === 0) {
        min = Math.min(min, DP[i / 3] + 1);
    }
    min = Math.min(min, DP[i - 1] + 1);
 
    DP[i] = min;
}
 
console.log(DP[N]);

초기값과 반복문 시작점

D[1] = 0만 설정하려면 반복문을 2부터 시작해야 한다. 아래 구현처럼 4부터 시작할 때는 D[2], D[3]도 먼저 채워야 한다.

복잡도

시간 O(N), DP 테이블 공간 O(N). 현재 코드는 문제의 최대 입력에 맞춰 1,000,001칸을 미리 할당한다.

참고 자료

바킹독의 실전 알고리즘 0x10강 — 다이나믹 프로그래밍을 학습하며 작성한 메모를 바탕으로 정리했다. 포함된 이미지는 원본 학습 메모의 캡처다.