어떤 동물원에 가로로 두칸 세로로 N칸인 아래와 같은 우리가 있다.
이 동물원에는 사자들이 살고 있는데 사자들을 우리에 가둘 때, 가로로도 세로로도 붙어 있게 배치할 수는 없다. 이 동물원 조련사는 사자들의 배치 문제 때문에 골머리를 앓고 있다.
동물원 조련사의 머리가 아프지 않도록 우리가 2*N 배열에 사자를 배치하는 경우의 수가 몇 가지인지를 알아내는 프로그램을 작성해 주도록 하자. 사자를 한 마리도 배치하지 않는 경우도 하나의 경우의 수로 친다고 가정한다.
이 문제는 2차원 배열으로 메모이제이션 기법을 이용하여 푸는 문제이다. 핵심은 현재 줄의 경우와 이전 줄의 경우를 합하는 것인데 합할 때 이전 줄이 무엇이었냐에 따라 현재 줄의 경우의 수가 바뀐다. 이전 줄의 경우는 3가지로 나뉠 수 있다. 사자가 없는 경우(0), 왼쪽에만 있는 경우(1), 오른쪽에만 있는 경우(2)이다. 따라서 각 경우에 따라 배열에 모든 경우의 수를 저장해 합 해줄 것이다.
우선 n*3 크기의 2차원 배열을 만들어 준다. 3인 이유는 위에서 말했다시피 3가지 경우를 모두 담기 위함이다. 배열을 생성한 뒤에는 배열의 첫번째 줄에는 각각 한가지 경우(xx, ox, xo) 밖에 존재하지 않음으로 1로 초기화한다. 그 다음 for문으로 바텀업 방식으로 n까지 쌓아올린다. 그리고 3가지 경우에 대한 연산을 해준다.
⚠️ 값이 빠르게 커지므로 문제의 출력 조건인 %9901 연산을 해주는 것을 잊지 말자.
마지막으로 출력할 때 모든 경우의 합을 더 해주고 %9901 연산을 해주면 된다.
#include <iostream>
#define NONE 0
#define LEFT 1
#define RIGHT 2
#define MOD 9901
using namespace std;
int main(){
int n, dp[100001][3]={};
//입력
cin >> n;
//배열의 길이가 1일 때는 세가지의 경우의 수가 1개씩 있으므로 1로 초기화
//*배열의 시작을 0으로 할 수도 있지만 이해하기 편하게 하기 위해 1부터 시작하였다
dp[1][NONE]=dp[1][LEFT]=dp[1][RIGHT]=1;
//2부터 n까지 모든 이전의 경우의 수를 합하여 바텀업 방식으로 쌓아 올린다
for(int i = 2; i <= n; i++){
//사자가 없는 경우 이전 줄에 있는 모든 경우를 더할 수 있음
dp[i][NONE] = (dp[i-1][NONE]+dp[i-1][LEFT]+dp[i-1][RIGHT])%MOD;
//사자가 왼쪽에 있는 경우 이전 줄에 사자가 왼쪽에 있는 경우를 제외하고 더해준다
dp[i][LEFT] = (dp[i-1][NONE]+dp[i-1][RIGHT])%MOD; //사자가 왼쪽에 있는 경우
//사자가 오른쪽에 있는 경우 이전 줄에 사자가 오른쪽에 있는 경우를 제외하고 더해준다
dp[i][RIGHT] = (dp[i-1][NONE]+dp[i-1][LEFT])%MOD;
}
//모든 경우의 수를 합한 뒤 출력
cout<<(dp[n][NONE]+dp[n][LEFT]+dp[n][RIGHT])%MOD;
}