백준의 쉬운 계단 수(10844) 문제이다.
[ 문제 바로가기 ]
[ 문제풀이 ]
간단한 숫자들 부터 어떤 규칙이 있는지 한번 찾아보자.
먼저, 한 자리 숫자들 중에서 계단 수는 몇개가 있을까 ?? 바로 9 개이다.
1 ~ 9 는 그 자체만으로 계단 수가 되고, 문제에서 0으로 시작되는 숫자는 없다고 했으므로, 0을 뺀 1 ~ 9까지 9개의 숫자가 한 자리 숫자들 중에서 계단 수가 된다.
그럼 두 자리 숫자들 중에서 계단 수의 갯수를 찾아보자.
하나하나 다 적어보자 !
10 / 12 / 21 / 23 / 32 / 34 / 43 / 45 / 54 / 56 / 65 / 67 / 76 / 78 / 87 / 89 / 98
이렇게 총 17개가 있다. 그럼 위의 17개의 숫자를 일단 대략적으로 기억해두자.
그 다음 세 자리 숫자들 중에서 계단 수의 갯수를 찾아보자.
이번에는 하나하나 다 적지는 않고 "일부만" 적어볼 것이다. 왜냐하면 세 자리 숫자들 중 계단 수가 32개가 되는데, 다 적어보고 싶지 않다...
대략적으로 몇개만 적어보면 다음과 같은 숫자들이 있다.
101 / 121 / 123 / 210 / 212 / 321 / 324 .... 뭐 이런식으로 쭉 있을 것이다.
그럼 두 자리 숫자들 중 계단 수와 세 자리 숫자들 중 계단수를 가져와보고 이를 통해 규칙을 한번 찾아보자.
10 / 12 / 21 / 23 / 32 / 34 / 43 / 45 / 54 / 56 / 65 / 67 / 76 / 78 / 87 / 89 / 98
101 / 121 / 123 / 210 / 212 / 321 / 324 ....
위의 숫자들 중에서 유독 특별한 숫자 2개가 존재한다.
바로 '0' 과 '9' 이다. 왜 특별한 숫자들일까 ??
두 자리 숫자들 중에서 가장 마지막에 있는 '98' 과 , 나머지 숫자인 '10' ~ '89' 를 비교해보자.
한 자리 숫자들 중에서 계단 수인 1 ~ 9 에서 , 두 자리 숫자들로 넘어오는 과정에서 보면 1 ~ 8은 모두 2개씩 가졌다.
1 같은 경우는 10 / 12
2 같은 경우는 21 / 23
3 같은 경우는 32 / 34 이런식으로 !
그런데 ! 유일하게 '9'만 보면, '98' 로 한 가지 숫자밖에 가지질 못했다.
그리고 세 자리 숫자들 중에서, 가장 첫 번째 있는 '101'과 나머지 숫자들을 비교해보자.
'121, '123'과 같이, 두 자리 숫자 중에서, '0'이 아닌 다른 숫자로 끝나는 숫자들에서는 세 자리 숫자로 넘어오면서 2가지 숫자들을 가졌다. 그런데 ! 유일하게 '0'만 '101'로 뒤에 '1'이 붙는 한 가지 경우밖에 가지지 못한다.
즉 ! 우리는 여기서 알 수 정보들을 정리해보면 다음과 같다.
1. 계단 수는 "이전 자릿수에서 '어떤 숫자'로 끝나는지에 영향을 미친다"
2. 그 '어떤 숫자'가 0이냐, 9냐, 그게 아니냐 로 나눠볼 수가 있다.
이렇게 2가지 정보를 알 수가 있다.
지금부터 '어떤 숫자' 라는 표현을 사용할 것인데, '어떤 숫자'라는 표현에 대한 의미를 정리해보면
"이전 자릿수에서 만들어질 수 있는 계단 수에서 마지막으로 끝나는 숫자" 를 의미한다.
그럼 지금부터는 일반화된 식을 찾아보기 위해서 간단한 변수들을 사용해서 표현해보자.
우리에게 필요한 변수는, 자릿수를 나타내는 변수와 , 가장 마지막 숫자를 나타내는 변수가 필요하다.
자릿수를 나타내는 변수 = a , 가장 마지막 숫자 = b로 나타내면 다음과 같이 표현할 수 있다.
"a 자리 숫자중에서 , 가장 마지막 숫자가 b인 쉬운 계단 수의 경우의 수는 몇 개인가 ?"
먼저, a = 1 인 경우에는 , 한 자리 숫자를 의미하고 , b가 1 ~ 9에 한해서(0 제외) 모두 '1'이라는 값을 가질 것이다.
a = 2인 경우부터는 위에서 말했듯이 가장 마지막 숫자가 '어떤 숫자'인지에 따라 갈라지므로 나눠주어야 한다.
1. 어떤 숫자 = 0 인 경우.
2. 어떤 숫자 = 9 인 경우.
3. 어떤 숫자 = 1 ~ 8인 경우.
이렇게 3가지로 나눠볼 수 있다.
1번 Case를 살펴보자. 어떤 숫자가 '0'인 경우이다. 즉 ! 이전 자릿수인 한 자리 숫자로 이루어진 계단 수에서 '0'으로 끝나는 경우' 를 의미한다. 이 경우는 존재하지 않는다. 왜냐하면 한 자리 숫자에서 '0'으로 끝나는 계단 수는 없기 때문이다.
2번 Case를 살펴보자. 어떤 숫자가 '9'인 경우이다. 다시 말하면, 이전 자릿수인 한 자리 숫자로 이루어진 계단 수에서 '9'로 끝나는 경우 를 의미한다. 이 경우는 1가지가 존재한다. 왜냐하면 '9'는 그 자체만으로 계단 수 이고, 한 자리 숫자에서 마지막으로 끝나는 숫자가 '9'이기 때문에 1가지가 존재한다고 말할 수 있다.
3번 Case를 살펴보자. 이 경우에는 2가지씩 존재한다. 바로 "어떤 숫자 - 1" , "어떤 숫자 + 1" 한 경우 이렇게 2가지씩 존재한다.
조금만 더 일반적인 표현을 사용해보자.
Func[a , b] 로 표현을 해보자. Func[a , b]의 의미는 "a자리 숫자에서 마지막 숫자가 b인 경우 발생할 수 있는 계단 수" 이다.
그럼 우리가 위에서 이야기했던 내용들을 위의 식으로 표현해보면...
1. 어떤 숫자 = 0인 경우 = Func[2 , 0]
2. 어떤 숫자 = 9인 경우 = Func[2 , 9]
3. 어떤 숫자 = 1 ~ 8인 경우 = Func[2 , (1 ~ 8) +- 1 ] 이 된다.
3번 Case부터 결과값을 구해보면, Func[2 , x] = Func[1 , x - 1] + Func[1 , x + 1] 이 된다.
왜냐하면, 어떤 숫자가 0 or 9가 아닌 경우, 발생할 수 있는 계단 수는 "이전 자릿수에서 발생할 수 있는 계단 수에서 가장 마지막 숫자가 x - 1 이거나 , x + 1인 경우" 만이 존재하기 때문이다. 따라서 , 위와 같이 2가지 경우를 더해준 값이 결과값이 된다.
1번 Case는 결과값을 구해보면, Func[2 , 0] = Func[1 , 1] 이 된다.
왜 ?? "두 자리 숫자에서 마지막 숫자가 0일 때, 발생할 수 있는 계단 수는, 한 자리 숫자에서 마지막 숫자가 1일 때, 발생할 수 있는 계단 수와 동일" 하기 때문이다.
동일한 이유는 간단하게 생각하면 된다. 두 자리 숫자에서 마지막 숫자가 0인 경우는 '10' 을 의미하고 , 한 자리 숫자에서 마지막 숫자가 '1'인 경우는 '1'을 의미한다. 즉 ! '1' 1가지 경우에 그 뒤에 '0'이 붙는 꼴이기 때문에 그대로 1가지 경우만 존재하는 것이다.
2번 Case도 1번Case와 동일하게 계산이 된다. Func[2 , 9] = Func[1 , 8] 이 된다.
두 자리 숫자에서 9로 끝나는 경우, '89' 가 있는데, 이는 한 자리 숫자에서 '8'로 끝나는 경우인 '8'에서 뒤에 '9'가 붙은 꼴이기 때문에 그대로 1가지 경우만 존재한다는 것이다.
이를 토대로 일반화된 식을 구해보면 다음과 같다.
Func[a , 0] = Func[a - 1 , 1]
Func[a , 9] = Func[a - 1 , 8]
Func[a , x] = Func[a - 1 , x - 1] + Func[a - 1 , x + 1] 이 된다.
위의 식을 코드로 표현하기 위해서 본인은 2차원 배열을 하나 사용해주었다.
DP[a][b] = c 의 의미는 "a자리 숫자에서 마지막 숫자가 b인 계단 수의 갯수는 c개입니다." 를 의미한다.
위와 같이 점화식을 적용시켜보면
DP[a][0] = DP[a - 1][1]
DP[a][9] = DP[a - 1][8]
DP[a][x] = DP[a - 1][x - 1] + DP[a - 1][x + 1] 이 된다.
그리고 문제에서 MODULER 연산을 진행하라고 되어있는데 , 이는 숫자가 너무 커지는 것을 방지하기 위해서이다.
또한, 최종 결과값이 아닌 중간 계산 과정에서도 숫자가 충분히 커질 수 있기 때문에 , 중간 과정에서도 MODULER연산을 진행해 주어야 한다. 그렇지 않으면 값이 너무 커져서 제대로된 계산이 제대로 되지 않는 경우가 발생할 수 있다.
[ 소스코드 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include <iostream> #define endl "\n" #define MAX 105 #define MODULER 1000000000 using namespace std; int N; long long DP[MAX][10]; // DP[a][b] = c 길이가 a인 숫자에서, 마지막 자리가 b로 끝나는 쉬운 계단 수는 총 c개가 있다. void Input() { cin >> N; } void Solution() { for (int i = 1; i <= 9; i++) DP[1][i] = 1; for (int i = 2; i <= N; i++) { for (int j = 0; j <= 9; j++) { if (j == 0) DP[i][j] = DP[i - 1][1]; else if (j == 9) DP[i][j] = DP[i - 1][8]; else DP[i][j] = DP[i - 1][j - 1] + DP[i - 1][j + 1]; DP[i][j] = DP[i][j] % MODULER; } } long long Answer = 0; for (int i = 0; i <= 9; i++) Answer = Answer + DP[N][i]; Answer = Answer % MODULER; cout << Answer << endl; } void Solve() { Input(); Solution(); } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
'[ BOJ Code ] > # BOJ -' 카테고리의 다른 글
[ 백준 9461 ] 파도반 수열 (C++) (2) | 2020.08.05 |
---|---|
[ 백준 11053 ] 가장 긴 증가하는 부분 수열 (C++) (4) | 2020.08.04 |
[ 백준 1912 ] 연속합 (C++) (0) | 2020.08.03 |
[ 백준 2156 ] 포도주 시식 (C++) (11) | 2020.07.29 |
[ 백준 1932 ] 정수삼각형 (C++) (0) | 2020.07.29 |