728x90
728x90
시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
0.15 초 (하단 참고) | 128 MB | 230137 | 75838 | 48572 | 32.294% |
문제
정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.
- X가 3으로 나누어 떨어지면, 3으로 나눈다.
- X가 2로 나누어 떨어지면, 2로 나눈다.
- 1을 뺀다.
정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.
입력
첫째 줄에 1보다 크거나 같고, 보다 작거나 같은 정수 N이 주어진다.
출력
첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.
예제 입력 1
2
예제 출력 1
1
예제 입력 2
10
예제 출력 2
3
힌트
10의 경우에 10 -> 9 -> 3 -> 1 로 3번 만에 만들 수 있다.
알고리즘 분류
- 다이나믹 프로그래밍
시간 제한
- Python 3: 1.5 초
- PyPy3: 0.7 초
- Python 2: 1.5 초
- PyPy2: 0.7 초
문제 출처
https://www.acmicpc.net/problem/1463
1463번: 1로 만들기
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.
www.acmicpc.net
문제 해결 방법
- 시간 제한 0.15초의 문제로, DP를 이용하여 빠르게 문제를 해결할 수 있다.
dp[n]
은n
을 1로 만들 수 있는 연산 횟수의 최솟값이라고 가정한다.
점화식 구하기
- 우선, 문제에 주어진 가능한 연산은 다음과 같다.
[연산 1] X가 3으로 나누어 떨어지면, 3으로 나눈다. [연산 2] X가 2로 나누어 떨어지면, 2로 나눈다. [연산 3] 1을 뺀다. |
- DP는 Bottom-Up 방식의 문제 해결 방법이므로,
n = 1
부터n = 3
까지 기본적인 값들을 미리 구해놓고 순차적으로 계산해본다.
dp[1] = 0; // X dp[2] = 1; // [연산2] dp[3] = 1; // [연산1]
예 : 1부터 10까지의 최소 연산 횟수 구하기
- 문제에서 주어진 연산을 이용하여 최소의 연산 횟수를 구할 수 있는 경우를 살펴보면 다음과 같다.
n = 1
일 때,1
을 1로 만들 수 있는 최소의 연산은 없다.- 따라서
dp[1] = 0
이다.
- 따라서
n = 2
일 때,2
를 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 2] (1가지)
dp[2] = 1
n = 3
일 때,3
을 1로 만들 수 있는 최소의 연산은 다음과 같다.- [연산 1] (1가지)
dp[3] = 1
n = 4
일 때,4
를 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 2] + [연산 2] (2가지)
- [연산 3] + [연산 1] (2가지)
dp[4] = 2
n = 5
일 때,5
를 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 3] + [연산 3] + [연산 1] (3가지)
- [연산 3] + [연산 2] + [연산 2] (3가지)
dp[5] = 3
n = 6
일 때,6
을 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 1] + [연산 2] (2가지)
- [연산 2] + [연산 1] (2가지)
dp[6] = 2
n = 7
일 때,7
을 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 3] + [연산 1] + [연산 2] (3가지)
- [연산 3] + [연산 2] + [연산 1] (3가지)
dp[7] = 3
n = 8
일 때,8
을 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 2] + [연산 2] + [연산 2] (3가지)
dp[8] = 3
n = 9
일 때,9
를 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 1] + [연산 1] (2가지)
- [연산 3] + [연산 2] + [연산 2] + [연산 2] (4가지)
dp[9] = 2
n = 10
일 때,10
을 1로 만들 수 있는 최소의 연산은 다음과 같다.
- [연산 3] + [연산 1] + [연산 3] (3가지)
- [연산 2] + [연산 3] + [연산 2] + [연산2] (4가지)
dp[10] = 3
- 표로 정리하면 다음과 같다.
n | dp[n] (최소 연산 횟수) |
1 | 0 |
2 | 1 |
3 | 1 |
4 | 2 |
5 | 3 |
6 | 2 |
7 | 3 |
8 | 3 |
9 | 2 |
10 | 3 |
- 따라서
dp[n]
에 다음의 두 연산 중 최솟값을 선택해주면 된다..
① dp[n - 1] + 1 ② dp[i / 2] + 1 or dp[i / 3] + 1 |
- 이를 토대로 점화식을 구하면 다음과 같다.
- 코드로 표현하면 다음과 같다.
int Solution(int n) { dp[1] = 0; for (int i = 2; i <= n; i++) { dp[i] = dp[i - 1] + 1; if (i % 2 == 0) { // 2로 나누어 떨어질 경우 dp[i] = min(dp[i], dp[i / 2] + 1); } if (i % 3 == 0) { // 3으로 나누어 떨어질 경우 dp[i] = min(dp[i], dp[i / 3] + 1); } } return dp[n]; }
- 쉽게 이해해보자.
dp[4]
를 구한다고 해보자. dp[4]
는 다음과 같이 2가지 방법으로 구할 수 있으며, 두 방법 중 최솟값을 선택해준다.- ① 방법 1 :
dp[n] = dp[n - 1] + 1
dp[4] = dp[4 - 1] + 1 = dp[3] + 1
dp[3] = dp[4] - 1
- 4를 구하는 최소 연산 횟수에서 연산 횟수 1을 빼주면 된다. (1을 빼준다. 즉, [연산 3] 수행)
- 3을 1로 만드는 최소 연산 횟수는 4를 1로 만드는 최소 연산 횟수에서 1을 빼주면 된다.
- dp[3] = 1 ([연산 1])
- dp[4] = 2 (
[연산 3]+ [연산 1])
- ② 방법 2 :
dp[n] = dp[n / 2(3)] + 1
dp[4] = dp[4 / 2] + 1 = dp[2] + 1
(2로 나누어 떨어지는 경우)dp[2] = dp[4] - 1
- 4를 구하는 최소 연산 횟수에서 연산 횟수 1을 빼준다.(2로 나눠준다. 즉, [연산 2] 수행)
- 2를 1로 만드는 최소 연산 횟수는 4를 1로 만드는 최소 연산 횟수에서 1을 빼주면 된다.
- dp[2] = 1 ([연산 2])
- dp[4] = 2 (
[연산 2]+ [연산 2])
dp[4] = dp[4 / 3] + 1
(3으로 나누어 떨어지는 경우)dp[4 / 3] = dp[4] - 1
- 4를 구하는 최소 연산 횟수에서 연산 횟수 1을 빼준다. (3으로 나눠준다. 즉, [연산 1] 수행)
- ① 방법 1 :
- 즉,
n
이2
또는3
으로 나누어 떨어지면방법 ①
과방법 ②
를 모두 수행해야 하므로,dp[n - 1] + 1
과dp[n / 2] - 1
(또는dp[n / 3] + 1
)의 최솟값을dp[n]
으로 지정한다. - 그렇지 않을 경우,
방법 ②
를 수행할 필요가 없으므로,dp[n - 1] + 1
을dp[n]
으로 지정한다.
코드
#include <iostream> #include <cmath> using namespace std; int N; int dp[1000001]; void Input() { cin >> N; } int Solution(int n) { dp[1] = 0; for (int i = 2; i <= n; i++) { dp[i] = dp[i - 1] + 1; if (i % 2 == 0) { // 2로 나누어 떨어질 경우 dp[i] = min(dp[i], dp[i / 2] + 1); } if (i % 3 == 0) { // 3으로 나누어 떨어질 경우 dp[i] = min(dp[i], dp[i / 3] + 1); } } return dp[n]; } void Output() { cout << Solution(N) << '\n'; } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); Input(); Output(); return 0; }
채점 결과

참고
- [단계별로 풀어보기] > [동적 계획법 1]
- 실버III
728x90
728x90
'Problem Solving > BOJ' 카테고리의 다른 글
[BOJ-16499][C++] 동일한 단어 그룹화하기 (2) | 2022.12.14 |
---|---|
[BOJ-11053][C++] 가장 긴 증가하는 부분 수열 (0) | 2022.12.14 |
[BOJ-2156][C++] 포도주 시식 (0) | 2022.12.13 |
[BOJ-10844][C++] 쉬운 계단 수 (0) | 2022.12.12 |
[BOJ-17427][C++] 약수의 합 2 (0) | 2022.12.11 |
[BOJ-2579][C++] 계단 오르기 (0) | 2022.12.09 |
[BOJ-1932][C++] 정수 삼각형 (0) | 2022.12.08 |
[BOJ-1149][C++] RGB거리 (0) | 2022.12.08 |