백준 1463번 "1로 만들기"
Question:
정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.
- X가 3으로 나누어 떨어지면, 3으로 나눈다.
- X가 2로 나누어 떨어지면, 2로 나눈다.
- 1을 뺀다.
정수 N이 주어졌을 때,
위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다.
연산을 사용하는 횟수의 최솟값을 출력하시오.
Input:
첫째 줄에 1보다 크거나 같고,
106보다 작거나 같은 정수 N이 주어진다.
Output:
첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.
Restriction:
시간제한:
0.15 초
Example Case:
Input case 1:
2
Output case 1:
1
Input case 2:
10
Output case 2:
3
풀이 과정:
이 문제는,
Dynamic Programming(DP)를 이용하여 풀 수 있는 문제이다.
정수 N에 대하여,
문제 조건에 대해 구현할 수 있는 연산이 3가지가 존재한다.
N에 대하여 N-1,N/2,N/3 총 세가지의 연산을 할 수 있다.
문제는,
이 세 연산을 활용하여 N(N>=1)을 1로 만들어야하는데,
또한 1로 만들 시의 연산 횟수가 최솟값이 되어야한다.
그렇다면 간단히 예시를 들어보자.
ex.N = 4 일때,
- N은 2로 나눴을 때 나머지가 0이다. 따라서 2로 나눌 수 있고 연산을 적용하면 4 -> 2 -> 1 이다.(연산 2번.2로 나누기 + 2로 나누기).
- N에서 1을 빼게 되면,(연산 1번) N은 3이 되고 3으로 나눴을 때 나머지가 0이다. 따라서 3으로 나누게 되면 1로 만들어진다. 4 -> 3 -> 1 이다.(연산 총 2번.1 빼기 + 3로 나누기).
이 처럼 정수 N에 대하여,
2나 3으로 나누어 떨어질 때 나누기 연산을 통한 연산의 최솟값과,
1로 빼고 나서의 연산의 최솟값.
이 둘중의 최솟값이 정수 N에 대한 연산의 최솟값이 된다.
코드를 살펴보자.
코드상에서는
dp[i/2],dp[i/3],dp[i-1] 처럼,
dp의 특징인 작은 경우의 해답을 재이용하는 모습을 보여주고 있다.
ex) N = 4
4 -> 2 -> 1 이면,
결국 N = 4 일 때는 N = 2 일 때의 연산의 최솟값을 이용한다.
ex 2) N = 10
10 -> 9 -> 3 -> 1 이면,
결국 N = 10 일 때는 N = 9 일 때의 연산의 최솟값을 이용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <algorithm>
using namespace std;
int dp[1000001];
int main() {
int n; // input
cin >> n;
for(int i = 2;i<1000001;i++) {
dp[i] = dp[i-1] + 1;
// 1로 빼는 연산,이전 N의 연산 최솟값 이용.
if(i%2== 0) {
dp[i] = min(dp[i],dp[i/2]+1);
//이전 N의 연산 최솟값과 2로 나누어질 때의 연산- 둘중의 최솟값
}
if(i%3 == 0) {
dp[i] = min(dp[i],dp[i/3]+1);
//이전 N의 연산 최솟값과 3로 나누어질 때의 연산- 둘중의 최솟값
}
}
cout << dp[n] << endl;
return 0;
}