문제 링크 : https://www.acmicpc.net/problem/1463
문제
정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.
- X가 3으로 나누어 떨어지면, 3으로 나눈다.
- X가 2로 나누어 떨어지면, 2로 나눈다.
- 1을 뺀다.
정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.
입력
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.
출력
첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.
접근방식
바텀업 방식으로 풀었다.
아래 표에 N에 따른 연산 횟수를 적어봤다.
1은 연산이 필요없으니 0번, 2는 2로 한번 나눠서 1번, 3은 3으로 한번 나눠서 1번이다.
예를 들어서 입력한 N이 8라고 가정해 보자. 8일 때, 2가지 선택지가 존재한다. 2로 나누기 또는 1을 빼기.
각각의 경우에 대해서 살펴보자.
2를 나누는 경우) 8을 2로 나누면, 당연한 말이겠지만, 4이다. 그러면, 4일 때의 최소 연산 횟수는 얼마일까. 앞의 표에 나온 것 처럼 2번이다. 따라서 8을 2으로 나누는 경우 1이 되는 연산 횟수는 3번이다.
1을 빼는 경우) 8에 1을 빼면 7이다. 7에서의 최소 연산은 3번이다. 따라서 총 연산 횟수는 1을 빼는 경우를 포함해서 4번의 연산을 하게 된다.
가능한 연산의 경우의 수 중에서 최소 연산 횟수를 택하면 되는 문제이다.
최소 연산을 구하는 함수를 "f"라고 가정해서 수식으로 나타내 본다면,
f(8) = min(f(8/2) , f(8-1))
위와 같다. 가능한 연산 경우의 수 중 작은 것을 택하면 되는 문제이다.
작은 수 부터 입력한 N까지 차례대로 구하면 되는 문제이다.
코드
#include <iostream>
#include<algorithm>
using namespace std;
int dp[1000001];
int main()
{
int x;
cin >> x;
for (int i = 2; i <= x; i++) {
//1을 빼는 경우
dp[i] = dp[i - 1] + 1;
//2로 나눠지는 경우
if (i % 2 == 0) {
dp[i] = min(dp[i], dp[i / 2] + 1);
}
//3으로 나눠지는 경우
if (i % 3 == 0) {
dp[i] = min(dp[i], dp[i / 3] + 1);
}
}
for (int i = 0; i < 10; i++)
cout << dp[i];
cout << dp[x] << '\n';
return 0;
}
느낀 점
다이나믹 프로그래밍에 익숙하지가 않아서, 처음 문제를 봤을 때 접근 방식을 생각하기가 어려웠다.
꾸준하게 공부하다 보면, 익숙해 지겠지...
다른 문제의 코드 : https://github.com/DaeeYong/Algorithm-Solution-
'알고리즘 > 백준 문제풀이' 카테고리의 다른 글
[C/C++]백준9095번 - 1,2,3 더하기 (0) | 2021.08.01 |
---|---|
[C/C++]백준 1003번 - 피보나치 함수 (0) | 2021.07.29 |
[C/C++]백준 2747번 - 피보나치 수 (0) | 2021.07.27 |
[C/C++]백준 2440문제 - 별찍기 - 3 (0) | 2021.07.26 |
[C/C++]백준: 1924번 2007년 (0) | 2021.07.26 |