공부/백준

1463번: 1로 만들기(백준 C++)

상연 2020. 11. 21. 18:21

목차

    www.acmicpc.net/problem/1463

     

    1463번: 1로 만들기

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

    www.acmicpc.net

    코드

    #include <iostream>
    using namespace std;
    
    int n;
    int dp[1000001];
    //각 숫자별 1이 될 때까지 수행해야하는 최소 단계 저장.
    
    int min(int a, int b){
    	return a < b ? a : b;
    }
    
    void solve(){
    	dp[1] = 0;
    	dp[2] = 1;
    	dp[3] = 1;
    	
    	for(int i=4; i<=n; i++){
    		if(i % 2 == 0 && i % 3 == 0){
    			// i가 2의 배수이자 3의 배수인 경우
    			dp[i] = min(min(dp[i / 2] + 1, dp[i / 3] + 1), dp[i - 1] + 1);
    		}
    		else if(i % 2 == 0){
    			// i가 2의 배수인 경우
    			dp[i] = min(dp[i / 2] + 1, dp[i - 1] + 1);
    		}
    		else if(i % 3 == 0){
    			// i가 3의 배수인 경우
    			dp[i] = min(dp[i / 3] + 1, dp[i - 1] + 1);
    		}
    		else{
    			// i가 2의 배수도 3의 배수도 아닌 경우, (-1)
    			dp[i] = dp[i-1] + 1;
    		}
    	}
    	cout << dp[n];
    }
    
    int main() {
    	cin >> n;
    	solve();
    	return 0;
    }

     

    문제

    동적계획법, 즉 DP를 사용하여 푸는 문제입니다.

    주어진 수 N을 3가지 연산을 활용하여 1로 만드는데 연산횟수가 최소로 될 때의 횟수가 몇인지 찾는 문제입니다.

    연산 방법에는

    1) 2로 나누어진다면 2로 나눈다.

    2) 3으로 나누어진다면 3으로 나눈다.

    3) 1로 뺀다.

     

    제 개인적인 생각으로 DP문제에 접근할때는 문제가 주는 방향의 반대로 거슬러 간다.

    라고 접근하시는 편이 풀이에 용이한 것 같습니다.

    무슨 의미인지 밑에서 예시를 통해 설명해 보겠습니다.

     

    풀이

    10의 경우에 10 -> 9-> 3-> 1로 3번 만에 만들 수 있다.

    위의 예제를 통해 알아 보겠습니다.

    우선, 그전에

     

    1이 1이 되기위해선 연산이 몇 번 필요할까요?

    0번 필요합니다.

    그렇다면 2와 3이 1이 되기위해선 몇 번 필요할까요?

    각각 2 또는 3으로 나누어 주면 되므로 1번 필요합니다.

     

    그러므로 각 수마다 연산에 필요한 수를 담는 DP 배열의 초기값은 아래와 같습니다.

     

    그렇다면 4부터 10까지 차근차근 연산의 최소 횟수를 구하면서 알아보도록 하겠습니다.

    N = 4

    4는 2로 나누어지는 수 입니다.

    따라서 주어진 연산하에서  4가 1에 가까워지는  방법에는 2로 나누거나, 1을 빼는 방법이 있겠습니다.

    1) 2로 나누는 경우

    4를 2로 나누어서 2가 됩니다.

    그런데 이미 저희는 DP[2] 의 값을 알고 있죠, 1입니다.

    이미 2에서 1이 되는 최소값은 알고 있으므로 그 최소값에 4에서 2로 가는 연산횟수 1을 추가해서

    DP[4]는 2가 됩니다.

     

    2) 1을 빼는 방법

    1)과 마찬가지입니다. 1을 빼면 3이되고

    우리는 DP[3]의 값을 알고 있으므로 1을 빼는연산횟수인 1을 DP[3]에 추가한 수인 2가

    DP[4] 가 되는 것입니다.

     

    그렇다면 이제 1)과 2)의 값을 비교해서 더 작은 값을 DP[4] 에 저장하면됩니다.

    이 경우에는 둘 다 2로 동일하므로 DP[4]는 2가 되겠습니다.

     

     

    N = 5

    5는 2로도, 3으로도 나누어지지 않는 수입니다.

    그렇기 때문에 5에서 4로 가는 방법 하나 밖에 존재하지 않죠.

    그렇기 때문에 기존 DP[4]의 값에서 + 1 된 값이 DP[5]가 되겠습니다.

     

    N = 6

    6은 2로도 3으로도 나누어지는 수입니다.

    그렇기 때문에 3가지 방법을 다 고려해서 최소값을 찾아야합니다.

    1) 2로 나누어지는 경우

    DP[6] = DP[3] + 1 = 2

    2) 3으로 나누어지는 경우

    DP[6] = DP[2] + 1 = 2

    3) 1을 빼는 경우

    DP[6] = DP[5] + 1 = 4

    3가지 방법 중 최소값은 2로, DP[6] 은 2가 되겠습니다.

     

    이제 점화식이 눈에 들어오실거라고 생각됩니다.

    그럼 빠르게 설명을 빼고 결과만 보도록 하겠습니다.

     

    N = 7

     

    N = 8

     

    N = 9

     

    N = 10

     

    이렇게 해서 10이 1이 되는 연산의 최소 횟수는 3임을 알 수 있습니다.

     

    즉 주어진 수 10부터 시작해서 1로 가는것이 아닌,

    1부터 시작해서 각 수의 최소값에다가 +1 하고, 연산방법에 따라 어느 연산이 더 효율적인지 비교해가며 10에 도달했을때의 연산의 최소값을 출력하게 됩니다.

    '공부 > 백준' 카테고리의 다른 글

    백준 2156번: 포도주 시식(C++)  (0) 2020.11.23
    10844번: 쉬운 계단 수(백준 C++)  (0) 2020.11.22
    2579번: 계단 오르기(백준 C++)  (0) 2020.11.20
    1932번: 정수 삼각형(백준 C++)  (0) 2020.11.18
    1149번: RGB거리(백준 C++)  (0) 2020.11.17