갓생살기프로젝트

[백준/BOJ/C++] 1463번 1로 만들기 - Dynamic Programming 본문

차근차근 알고리즘/Dynamic Programming

[백준/BOJ/C++] 1463번 1로 만들기 - Dynamic Programming

Heeyeon.dev 2021. 1. 6. 23:16
728x90

1. 문제

정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.

1. X가 3으로 나누어 떨어지면, 3으로 나눈다.
2. X가 2로 나누어 떨어지면, 2로 나눈다.
3. 1을 뺀다.

정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.

2. 입력

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

3. 출력

첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.

4. 풀이

사용 변수 : N(입력받는 정수), arr 배열

연산 횟수를 최소한으로 하려면 1을 빼는것보다는 2와 3으로 나누는것이 유리하고, 2로 나누는것보다는 3으로 나누는게 유리하다.

보통은 그러한데 예시에서 봤을때  10의 경우는 10 -> 9 -> 3 -> 1로 3번만에 1로 만드는 것이 가능하다. 10은 2로 나누어지지만 1을 뺀 후에 3으로 나누는 것이 연산 횟수를 최소한으로 만드는데에 유리하다. 이런 경우는 다 확인할수가 없기 때문에 bottom - up 방식으로 문제를 해결한다.

*bottom - up 방식이란? - 작은 문제부터 값을 구해 전체의 값을 구하는 방식. 

1) 먼저 코드의 단순화를 위해 최소값을 구하는 min 함수를 정의해준다.

2) main 함수에서 N과 arr 배열을 선언하고, N을 입력받는다.(여기서 arr배열의 크기는 10^6 + 1로 하였는데, 입력값이 최대 10^6이기 때문이다.)

3) 먼저 arr[i] = arr[i - 1] + 1로 할당하는데 이는 i가 2나 3으로 나누어지지 않는경우이다.

예를 들어, i가 7일때 arr[7] = arr[6] + 1이다. 이는 거꾸로 생각했을 때 정수 7이 주어졌을 때 1을 빼서 숫자 6을 만든것과 같다. 

4) 다음으로 i가 2 또는 3으로 나눠질때 i에서 1을 빼는것과 나누는것 중 연산횟수가 더 작은 것을 배열에 대입한다.

5) 위의 for문으로 각 숫자별로 해당하는 최소연산값을 배열에 대입해놓았기 때문에 arr[N]으로 최소연산값을 출력한다.

5. 소스코드(C++)

#include <iostream>
using namespace std;
int min(int a, int b){
    return a < b ? a : b;
}

int main(){
    int N;
    int arr[1000001] = {0, };
    cin >> N;

    for(int i = 2; i <= N; i++){
        arr[i] = arr[i - 1] + 1;
        if(i%2 == 0){
            arr[i] = min(arr[i], arr[i/2] + 1);
        }
        if(i%3 == 0){
            arr[i] = min(arr[i], arr[i/3] + 1);
        }
    }
    cout<<arr[N];
    return 0;
}
728x90