위 테이블을 보면, 1은 만들 필요가 없으니까 0, 2와 3은 2로 3으로 나누어 떨어지고, 몫이 1이 나오니까 횟수가 1이다. 그렇다면 4부터는 어떻게 구할까?
| 인덱스 | 횟수 | | 4 | 3, 1 또는 2, 1 |
4를 1로 만드는 최솟값을 구해야 하는 경우, 4를 1로 뺐을 때 3이 되고, 3의 값은 1이므로 총 두 번이 된다. 하지만 4를 2로 나누는 경우 몫이 2가 되는데, 2의 값은 1이므로 3으로 나누었을 때와 마찬가지로 총 두 번이 된다. 이러나저러나 4를 1로 만드는 최솟값은 2가 되는 것이다.
| 인덱스 | 횟수 | | 5 | 4, 3, 1 또는 4, 2, 1 |
다시 5를 예로 들자면, 5는 3이나 2로 나누어 떨어지지 않으므로 무조건 1을 빼고 시작해야 한다. 5-1=4인데, 4의 횟수는 아까 구한 2이다. 따라서 5의 최솟값은 4의 연산 횟수(2) + 빼기 연산(1) = 3이 된다.
이렇게 10까지 반복하면 아래와 같은 테이블이 만들어지게 된다.
인덱스
횟수
1
0
2
1
3
1
4
3, 1 또는 2, 1
5
4, 3, 1
6
3, 1 또는 2, 1
7
6, 3, 1
8
4, 3, 1
9
3, 1
10
9, 3, 1
각 인덱스의 최솟값을 담은 배열을 A라고 했을 때, 점화식은 다음과 같다.
1을 뺐을 때: A[n - 1] + 1
n % 2 == 0일 때: A[n / 2 == 0] + 1
n % 3 == 0일 때: A[n / 3 == 0] + 1
횟수를 계산해야 하기 때문에 1을 더한다. 각 케이스의 값을 비교해 최소값을 찾아내서 A[n] 배열에 넣어주면 된다.
} return dp[n]; } publicstaticvoidmain(String args[])throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int N = Integer.parseInt(br.readLine()); dp = newint[N + 1]; System.out.println(dp(N));
} }
Code (Javascript)
역시 백준에서 자바스크립트 입력은… 노우…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
var fs = require('fs'); var input = fs.readFileSync('/dev/stdin').toString().split("\n"); var X = parseInt(input[0]);
// 1부터 3까지의 값을 미리 넣어준다. // 편의를 위해 인덱스는 1부터 시작한다 let dp = [0, 0, 1, 1];
for (let i = 4; i <= X; i++) { // -1을 뺄 때, 2로 나눌 때, 3으로 나눌 때의 값을 비교해 최소값을 알아냄 // -1을 했을 때 dp[i] = dp[i - 1] + 1; // 3으로 나누어 떨어지는 수일 때 if (i % 3 === 0) dp[i] = Math.min(dp[i], dp[i / 3] + 1); // 2로 나누어 떨어지는 수일 때 if (i % 2 === 0) dp[i] = Math.min(dp[i], dp[i / 2] + 1); }