Algorithm/백준

[백준] 11057 - 오르막 수

비번변경 2024. 4. 4. 18:27

문제

문제 : https://www.acmicpc.net/problem/11057

오르막 수란 수의 자릿수가 오름차순을 이루는 수를 말한다. 이때 인접한 두 수의 값이 같아도 오름차순을 준수하는 것으로 판단한다.

예로 들어 2234, 3678, 1119은 오르막 수이다. 하지만 2232, 3676, 91111은 오르막 수가 아니다.

수의 길이 N이 주어졌을 때 오르막 수의 개수를 구하여라. 수는 0으로 시작할 수 있다. 단, 결괏값은 10007로 나눈 수로 출력한다.

 

+ 다른 사람의 풀이를 참고했다.

 

접근

동적 계획법으로 접근하면 좋은 문제다.

 

수의 길이가 1인 경우, 오르막 수는 0, 1, 2, 3 …… 9가 해당된다.

수의 길이가 2인 경우, 첫번째 자리의 수에 따라 다음으로 올 수 있는 수의 개수는 다음과 같다.

자리 수 다음 자리 수 개수
0 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 10
1 1, 2, 3, 4, 5, 6, 7, 8, 9 9
2 2, 3, 4, 5, 6, 7, 8, 9 8
3 3, 4, 5, 6, 7, 8, 9 7

 

수의 길이가 3인 경우, 첫번째 자리의 수에 따라 다음으로 올 수 있는 수의 개수는 아래와 같다.

자리 수 다음 자리 수
0 00, 01, 002, …, 88, 89, 99
1 11, 112, 13, …, 88, 89, 99
2 22, 23, 24, …, 88, 89, 99
3 33, 34, 35, …, 88, 89, 99

즉, 수의 길이가 2인 오르막 수의 개수를 참조하여 계산할 수 있다.

예로 들어 수의 길이가 3이고 첫 번째 자리 수가 0인 오르막 수의 개수는 수의 길이가 2이고 첫번째 자리의 수가 0 이상 9 미만인 오르막 수의 총 개수이다.

수의 길이와 첫번째 자리 수에 따라 오르막 수의 개수를 표로 표현하면 아래와 같다.

이 점을 이용해 memoization으로 문제를 해결할 수 있다.

 

 

풀이

1. 메모이제이션에 사용할 테이블 초기화

인덱스가 자리의 수라고 가정하고, 수의 길이가 1인 오르막 수의 개수로 초기화했다.

memo = [[1 for i in range(10)]]

 

2. 나머지 연산에 사용할 수 선언

divided = 10007

 

3. 수의 길이 초기화

n = int(sys.stdin.readline())

 

4. 오르막 수 계산

각 자리 수의 오르막 수의 개수를 계산한다. 계산한 결과는 나머지 연산하여 저장하고 해당 연산은 테이블의 길이가 수의 길이와 동일해질 때까지 반복한다.

memo.append([sum(memo[-1][i:]) % divided for i in range(10)])

 

5. 결괏값 출력

마지막 테이블 원소의 합을 나머지 연산한 뒤 출력한다.

print(sum(memo[-1]) % divided)

 

 

전체 코드는 아래와 같다.

import sys

memo = [[1 for i in range(10)]]
divided = 10007

n = int(sys.stdin.readline())
for i in range(1, n):
    memo.append([sum(memo[-1][i:]) % divided for i in range(10)])
print(sum(memo[-1]) % divided)

 

 

참고 문서

https://minhamina.tistory.com/147