관리 메뉴

사과하는 제라스

[백준 BOJ 10844번] 쉬운 계단 수 본문

JAVA 백준 알고리즘 문제풀이/동적 계획법

[백준 BOJ 10844번] 쉬운 계단 수

Xerath(제라스) 2021. 12. 5. 19:17

목차

    728x90
    반응형

    출처 : https://www.acmicpc.net/problem/10844

     

    1. 문제

     

    45656이란 수를 보자.

    이 수는 인접한 모든 자리의 차이가 1이다. 이런 수를 계단 수라고 한다.

    N이 주어질 때, 길이가 N인 계단 수가 총 몇 개 있는지 구해보자. 0으로 시작하는 수는 계단수가 아니다.


    2. 입력

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

    1
    2

    3. 출력

    첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

    9
    17

    4. 풀이

    이 문제는 풀이에 있어서 다음 2가지 기본적인 규칙만 코드로 잘 구현해두면 작성하기 쉽다.

     

    1. 가장 큰자리에는 0이 올 수 없다. 2. 앞에 작성된 숫자가 9나 0이면 현재 자리수에는 8이나 1밖에 적을 수 없다.

     

    이들을 처음 시작하는 값이 가장 큰자리수이냐(Top-Down) 작은 자리수이냐(Bottom-Up)에 따라 구현할 코드의 방식이 바뀐다.


    5. 소스코드

    <Top-Down 방식>

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
     
    public class Main {
    	static Long [][] dp;
    	final static long MOD = 1000000000;
    	
    	public static void main(String[] args) throws IOException {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		
    		int N = Integer.parseInt(br.readLine());
    		dp = new Long [N+1][10];
    
            for(int i=0; i<10; i++) dp[1][i] = 1L; 
            //첫자리(1의자리)에는 어떤 값이 오든 1가지의 경우의 수임. 
            //왼쪽(큰자리수)에서 시작해서 오른쪽(작은자리수)으로 가짓수를 찾아갈 것이니 종착점의 경우의 수는 1가지이다.
            long result = 0;
    
            for(int i=1; i<10; i++){
                result += caseOf(N,i);
            }
    
            System.out.println(result%MOD);
        }
    
        public static long caseOf(int digit, int val){ //caseOf는 digit자릿수에 val이 왔을 때 앞에 자리들은 채우는 경우의 수가 어떻게 됨?을 리턴하는 함수이다.
            
            if(digit == 1) return dp[digit][val];
    
            if(dp[digit][val] == null){
                if(val == 0) dp[digit][val] = caseOf(digit-1, 1);
    
                else if(val == 9) dp[digit][val] = caseOf(digit-1, 8);
    
                else dp[digit][val] = caseOf(digit-1, val+1) + caseOf(digit-1, val-1);
            }
    
            return dp[digit][val]%MOD;
        }
    
    }

     

    <Bottom-Up방식>

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
     
    public class Main {
    	static Long [][] dp;
    	final static long MOD = 1000000000;
    	
    	public static void main(String[] args) throws IOException {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		
    		int N = Integer.parseInt(br.readLine());
    		dp = new Long [N+1][10];
            long result = 0;
            for(int i=0; i<10; i++) dp[1][i] = 1L; //첫자리(1의자리)에는 어떤 값이 오든 1가지의 경우의 수임. 오른쪽에서 시작해서 왼쪽으로 가짓수를 찾아갈 것인데 맨 마지막에는 이미 1개로 정해져 있어야 재귀를 통해 다시 원 위치로 돌아올 때 경우의 수를 찾아갈 수 있음.
    
            for(int i=2; i<=N; i++){
                for(int j=0; j<10; j++){
                    if(j == 0) dp[i][j] = dp[i-1][1]%MOD;
                    else if(j == 9) dp[i][j] = dp[i-1][8]%MOD;
                    else dp[i][j] = (dp[i-1][j-1] + dp[i-1][j+1])%MOD;
                }
            }
    
            for(int i=1; i<10; i++){
                result += dp[N][i];
            }
            System.out.println(result%MOD);
        }
    }

    6. 배운 것

    주어진 크기(1,000,000,000)를 넘기지 않게 하기 위해 미리 final 상수를 위에 기입해두고 필요할 때마다 나눠주는 모습을 기본적인 스킬로 가져가야겠다.

     

    또한, 이전의 DP문제에서도 다뤘었지만 위 <Top-Down방식>에서 쓰인 Long [][]처럼 박싱을 통해 입력받지 않은 것들에 대해 판별 시 null을 사용할 수 있도록 해야겠다.

    728x90
    반응형