문제
그림과 같이 2 n 크기의 직사각형을 2 1 크기의 타일로 채우려고 합니다. 타일들은 서로 겹쳐서는 안 되고, 90도로 회전해서 쓸 수 있습니다. 단 이 타일링 방법은 좌우 대칭이어서는 안 됩니다. 위 그림은 2 * 5 크기의 직사각형을 채우는 비대칭 타일링 방법 6가지를 보여줍니다. 다음의 2가지는 좌우대칭이기 때문에 세지 않습니다.
n 이 주어질 때 가능한 비대칭 타일링 방법의 수를 계산하는 프로그램을 작성하세요. 방법의 수는 매우 클 수 있으므로, 1,000,000,007 로 나눈 나머지를 출력합니다.입력
입력의 첫 줄에는 테스트 케이스의 수 C (1 <= C <= 50) 가 주어집니다. 그 후 각 줄에 사각형의 너비 n (1 <= n <= 100) 이 주어집니다.출력
각 테스트 케이스마다 한 줄에 비대칭 타일링 방법의 수를 1,000,000,007 로 나눈 나머지를 출력합니다.예제 입력
3 2 4 92 ``` __예제 출력__ ``` 0 2 913227494 ```
이 문제는 두 가지 방법 정도가 떠올랐는데,
1. 전체 타일링 방법의 수(2 x n) - 반절의 타일링 방법의 수(2 x (n / 2))
2. 대칭 형태를 제외하며 타일링 방법 count
필자는 1번 방법이 더 수월해 보였기 때문에, 1번 방법을 택하였다.
필자는 이 문제를 두 번째로 풀었는데, 이전 코드보다는 좀 더 효율적이 되었다고 생각하여 해당 풀이를 수정한다. 전체 타일링 방법의 수는 이전에 풀었던 문제인 TILING2와 동일하다.
int tiling(int n) {
if (n <= 1) return 1;
int& res = cache[n];
if (res != -1) return res;
return res = (tiling(n - 1) + tiling(n - 2)) % MOD;
}
n이 홀수일 경우와 짝수일 경우가 다른데, 홀수인 경우는 n / 2 지점에서(n = 5일 경우, idx = 2인 지점) 2 x 1짜리 타일을 한 개 깔아야 대칭이 된다.
n이 짝수인 경우는, n / 2 지점에서(n = 6인 경우, idx = 3인 지점) 2 x 1짜리 타일을 한개 깔거나, 1 x 2 타일을 두 개 깔때 대칭이 된다.
즉, 아래 그림과 같을 경우만 대칭이 된다.
x x | x x
x x | x x
x x ㅡ ㅡ x x
x x ㅡ ㅡ x x
x x | | x x
x x | | x x
우리는 이미 tiling() 함수를 통해서 모든 경우의 수를 구해놓았기 때문에,
짝수일 경우는 tiling(n) - tiling(n/2) - tiling(n/2 - 1),
홀수일 경우는 tiling(n) - tiling(n/2)를 해주면 되겠다.
다만 함정을 조심한다.
mod 연산을 하므로, tiling(n)이 tiling(n/2)보다 작을 수 있기 때문이다.
항상 이런 실수가 다 풀어놓고 틀리게 한다..
음수일 경우를 대비해서, -연산이 있다면 mod 연산의 제수를 더해준다.
전체적인 소스코드는 아래와 같다.
#include<iostream>
#include<cstring>
using namespace std;
const int MOD = 1000000007;
int cache[101];
int tiling(int n) {
if (n <= 1) return 1;
int& res = cache[n];
if (res != -1) return res;
return res = (tiling(n - 1) + tiling(n - 2)) % MOD;
}
int asymtiling(int n) {
if (n % 2 == 1) {
return (tiling(n) - tiling(n / 2) + MOD) % MOD;
}
else {
return ((tiling(n) - tiling(n / 2) + MOD) % MOD - tiling(n / 2 - 1) + MOD) % MOD;
}
}
int main() {
int testCase;
cin >> testCase;
for (int tc = 0; tc < testCase; tc++) {
memset(cache, -1, sizeof(cache));
int n;
cin >> n;
cout << asymtiling(n) << endl;
}
return 0;
}
작은 실수, 디테일한 부분에서 실수를 해서 크게 어렵지 않았는데도 오래 걸린 문제이다.
mod연산이나 자료형의 범위는 언제나 특히 잘 생각하면서 풀어야겠다.
두 번째 풀인데도 또 MOD연습에서 헤멜 뻔 했다. MOD연산 항상 유의하자.