백준 11726번 타일링 문제입니다.
DP 문제의 특징은 전에 계산된 식의 결과값을 다시 사용함으로써 메모리 효용성을 기대하는 것이죠.
그래서 문제에 대한 반복적인 규칙을 찾아
점화식을 만들어 푸는 방법으로 접근해야합니다.
문제 분석
$2*n$ 크기의 직사각형을 $1*2$ 혹은 $2*1$ 의 타일로 채우는 방법은 두 가지 경우의 수로 나눌 수 있습니다.
- 마지막 직사각형이 $1*2$ 타일이 한 개 들어가는 경우
- 마지막 직사각형으로 $2*1$ 타일이 두 개 들어가는 경우
첫 번째 경우는 마지막은 $1*2$ 타일로 놓고, $2*(n-1)$ 크기의 직사각형을 채우는 방법의 수와 같죠.
두 번째 경우는 마지막에 타일이 두 개가 들어가기 때문에 $2*(n-2)$ 크기의 직사각형을 채우는 방법의 수와 같아집니다.
그럼 이걸 계속해서 점화식으로 채우면
dp[n] = dp[n-1] + dp[n-2]
로 나타낼 수 있습니다.
그럼 먼저 $2*1$의 직사각형을 채우는 경우의 수를 볼게요.
세로의 길이가 2로 고정되어 있고, 가로의 길이가 1로 고정되어 있으므로 들어갈 수 있는 타일은 $2*1$ 오직 한 개 뿐 입니다.
dp[1] = 1
두 번째 경우를 봅시다.
$2*2$의 직사각형을 채워야 한다면, $1*2$ 짜리 타일을 두 개 깔 든, $2*1$ 짜리 타일을 두 개 깔 든 할 수 있습니다.
그럼 $1*2$ 타일만 쓰는 경우 1가지, $2*1$ 타일만 쓰는 경우 1가지 해서 총 2가지 경우의 수가 됩니다.
dp[2] = 2
그럼 앞에서 점화식을 생각했기 때문에 n이 3이상인 경우에는 직사각형에 끝에 세로로 긴 타일이 온다면 끝에 타일하나를 고정하고 $2*2$ 크기의 직사각형을 채우는 경우의 수를 생각할 수 있습니다. 위에서 확인했듯이 값은 dp[2] = 2가 되겠죠.
또 다른 경우로 가로로 긴 타일을 두개 배치해야 한다면 뒤에 타일 두개를 고정하고 $2*1$크기의 직사각형을 채우는 경우의 수만을 생각해 볼 수 있습니다. dp[1]의 값이 되겠네요.
본 점화식을 만족하기 때문에,
점화식을 바탕으로 코드를 짜줍니다.
정답 코드
n = int(input())
dp = [0] * (n+1)
dp[1] = 1
if n > 1 :
dp[2] = 2
# 1*2 타일을 마지막에 놓는 경우, 2*(n-1) 크기의 직사갹형을 채우는 방법의 수와 같다.
# 2*1 타일을 마지막에 놓는 경우, 2*(n-2) 크기의 직사각형을 채우는 방법의 수와 같다.
# dp[n] = dp[n-1] + dp[n-2]
for i in range(3, n+1) :
dp[i] = (dp[i-1] + dp[i-2]) % 10007
print(dp[n])
처음에는 dp[1] = 1, dp[2] = 2를 두고 배열값을 지정해두었으나,
n = 1 이 입력되는 경우, 인덱스 값은 1까지 밖에 돌지 않는데 dp[2] = 2를 지정하게 되니, 인덱스 오류가 발생하게 됩니다.
따라서 조건문을 생성하여
n 이 1보다 큰 경우를 고려해주도록 하였습니다.
그리고 n이 3이상인 경우,
반복문을 이용하여 점화식을 적용하였습니다.
오버플로우
이 문제는 오버플로우를 주의해서 풀이해야 합니다.
각 데이터 타입의 변수들은 고정된 크기의 비트로 구성됩니다.
즉, 담을 수 있는 양이 정해진 방들인 것이죠.
예를 들어 32비트 정수(int)형 변수의 경우 최대 $2^{32}-1$의 값을 가질 수 있습니다.
표현할 수 있는 숫자가 $2^{32}-1$ 까지인 것이죠.
따라서 이 변수에 표현할 수 있는 한계 숫자보다 더 큰 값을 저장하게 되면 양의 오버플로우가 발생해 -2,147,483,648 이 출력됩니다. (표현할 수 있는 가장 첫 번째 값으로 돌아감) 반대로 표현할 수 있는 한계 숫자보다 더 작은 값을 저장하게 되면 음의 오버플로우가 발생해 오히려 가장 큰 양수값이 출력되는 결과를 초래할 수 있는 것이죠.
Python의 경우는 정수형이 사용가능한 메모리가 허용하는 한 어떠한 크기의 숫자도 저장할 수 있도록 설계 되어 오버플로우가 발생하지 않지만, JAVA, C++과 같은 다른 언어들의 경우 정수형을 담을 수 있는 공간이 보다 작아 작은 숫자 범위내에서만 표현이 가능합니다.
따라서 작은 범위내에서만 계산되도록 이 문제에서는 'int' 타입의 범위 내에서 연산을 안전하게 수행하도록 10007로 나눈 나머지를 구하여 출력하도록 명시하였습니다.
출력시 print 구문에서 %10007을 해 줄 수도 있지만,
파이썬을 제외한 다른 언어들에서는 dp 배열 계산이 이뤄지는 점화식 과정에서 오버플로우가 발생해 문제의 결과값이 달라질 수 있으므로 점화식 자체에서 애초에 %10007을 계산하여 저장해주는 것이 좋습니다.
'알고리즘과 자료구조 > 매일매일 알고리즘' 카테고리의 다른 글
[백준] 10799 번 쇠막대기 (스택) (3) | 2024.04.04 |
---|---|
[백준] 10828번 스택 (1) | 2024.01.23 |
[백준] 9012번 괄호 (1) | 2024.01.23 |
11. 카드 역배치 (0) | 2024.01.04 |
10. 문자열에서 숫자만 추출하기 (1) | 2024.01.04 |