ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 1038번 : 감소하는 수
    Programming/문제풀이 2025. 10. 20. 00:15

    https://www.acmicpc.net/problem/1038

     

     

    💡 접근법

    이전에 사전 문제를 풀었던게 생각나서 그 문제랑 비슷하게 풀었다 ( https://www.acmicpc.net/problem/1256 )

     

    우선 이 문제를 두 단계로 분리하자면

    1. 지금 내가 찾는 숫자가 어떤 범위에 있는지 와 2. 실제 숫자가 뭔지

    를 찾는 두 단계로 나눠야 한다

     

    1. 지금 내가 찾는 숫자가 어떤 범위에 있는지 찾기

    기본적으로 한 숫자가 정해지면, 그 뒤의 숫자들은 이 앞자리 숫자에 종속이 된다

    경우의 수들을 쭉 쓰다보면, 5421 라는 감소수는 4가 가장 앞자리인 숫자의 맨앞에 5를 더한 것과 같다

    5가 가장 앞자리인 4자리 숫자들 중, 그 다음 수가 4___ 인 경우의 수는, 4가 가장 앞자리인 3자리 수와 같다

    즉 DP 식으로 정리하자면

    • 지금 몇자리수인지 i, 가장 앞자리 숫자가 뭔지 j 에 대해. k를 반복문으로 돌리면
    • DP[i][j] = DP[i-1][k] 와 같다

     

    조금 더럽지만.. 내가 찾을 때 끄적거린 기록

     

    DP 식을 먼저 구해서 찾고자 하는 숫자가 몇자리 수의, 가장 맨 앞자리가 어떤 숫자인지 찾을 수 있다

     

    2. 실제 숫자가 뭔지

    가장 맨 앞자리가 뭔지 알았다면, 그 다음 숫자부터는 자릿수를 하나씩 줄여가면서 1번을 반복하면 된다

     

    18번째 숫자를 찾고자 하는 경우,

    2번째 자릿수가 4 인 숫자의 범위는 16~19 이기 때문에 여기서 자릿수를 내린다

     

    1번째 자릿수가 0인 숫자의 범위는 16~16이기 때문에 스킵하고 증가시키고

    1번째 자릿수가 1인 숫자의 범위는 17~17이기 때문에 스킵하고 증가시키고

    1번째 자릿수가 2인 숫자의 범위는 18~18이기 때문에 찾는 숫자와 같으므로 정답을 출력한다

     

    이 알고리즘을 코드로 구현하면 된다

     

    • 숫자의 범위를 알았어도, 뒷자리가 다 채워지지 않았을 수 있으므로 그 경우도 체크해야 한다

     

    ➕ 짚고 넘어가기

     

    🧪 테스트 케이스

    입력 출력
    1022 9876543210
    1023 -1

     


    ✏️ 나의 풀이

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int DP[11][11]; // 자릿수 r 일때 앞자리 c인 경우의 수
    int main() {
        int TargetNum;
        cin >> TargetNum;
    
        if (TargetNum == 0)
        {
            cout << 0 << "\n";
            return 0;
        }
    
        int MaxValue = 0;
        for (int i = 0;i < 10;i++)
        {
            DP[1][i] = 1;
        }
    
        for (int i = 2; i < 11;i++)
        {
            for (int j = 1; j < 10;j++)
            {
                for (int k = 0;k < j;k++)
                {
                    DP[i][j] += DP[i - 1][k];
                }
            }
        }
    
        int Count = -1;
        long long Answer = 0;
        for (int i = 1;i < 11;i++)
        {
            for (int j = 0;j < 10;)
            {
                if (DP[i][j] == 0) 
                {
                    j++;
                    continue;
                }
                if (Count + DP[i][j] >= TargetNum)
                {
                    //지금 앞자리에 대해 타겟이 안에 있으면 숫자찾기
                    Answer = Answer * 10 + j;
    
                    if (i == 1 && Count + DP[i][j] == TargetNum)
                    {
                        //뒷자리수까지 다 채워졌는지 확인
                        cout << Answer << "\n";
                        return 0;
                    }
                    else
                    {
                        i--;
                        j = 0;
                    }
                }
                else
                {
                    //없으면 다음으로 넘어가기
                    Count += DP[i][j];
                    j++;
                }
            }
        }
    
        cout << -1 << "\n";
    
        return 0;
    }

     

    ✏️ 다른 사람의 풀이

     

    실제 개수가 많지 않아서 단순 DFS로 풀어도 풀리는 문제였다

    평상시였으면 백트래킹으로 먼저 생각했을 것 같은데, 이전에 DP로 풀었더니 생각이 이렇게 박혀버림..

     

     

    ---

     

    코드 구현하다가 은근 시간을 많이 잡아먹었다

    미리 생각 다 해놓고 시작해도 짜다보면 구현이 많이 막히는듯.. 구현 연습을 더 많이 해야한다

     

    728x90

    'Programming > 문제풀이' 카테고리의 다른 글

    [백준] 1005번 : ACM Craft  (0) 2025.10.18
    [백준] 2295번 : 세 수의 합  (0) 2025.10.10
Designed by Tistory.