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
반응형