문제 링크

발상

구조상 첫 인덱스와 마지막 인덱스가 연결되어있는 환형 구조의 문제다. 볼록 다각형 대각선 긋기 문제로 치환해서 생각하면 조금 더 편하다. 볼록 다각형도 인접한 꼭짓점을 그어도 대각선이 생기지 않는다. 가능한 경우는 어떤 인덱스를 선택하냐/선택하지 않냐 2가지 경우 밖에 없다. 어떤 인덱스 를 선택했다면 , 은 선택이 불가능하다. 반대로 를 선택하지 않았다면 를 제외한 모든 인덱스를 선택할 수 있다. 즉, 의 선택 여부에 따라 두 가지 경우로 나눠 재귀 구조의 부분 문제를 형성할 수 있다. 문제를 재귀구조로 분할했으니 다음으로 기저사례를 찾아보자.

서로 이웃하는 인덱스를 선택할 수 없으므로 비둘기집 원리에 의해 를 초과하는 인덱스를 선택할 수 없다. 따라서 이면 경우의 수는 반드시 0이다. 다음으로 인 경우다. 이웃하는 경우가 없으므로 자명하게 경우의 수는 이다.

점화식

은 어떤 인덱스 를 선택한 경우이다. 이 때, 이웃한 2개의 인덱스는 선택이 불가능해지므로 전체 수 중에서 이미 선택한 를 제외한 개의 인덱스를 선택하는 부분 문제가 발생한다. 은 어떤 인덱스 를 선택하지 않은 경우이다. 이 때는 별도의 인덱스 제외 없이 를 제외한 모든 인덱스를 선택할 수 있으므로 전체 수 개 중에서 다시 개를 선택하는 부분 문제가 발생한다.

코드 ( Top-Down )

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int mod = 1e9 + 3;
vector<vector<int>> dp;
int r(int n, int k) {	
	if (dp[n][k] != -1) return dp[n][k];
	if (k * 2 > n) return dp[n][k] = 0;
	if (k == 1) return dp[n][k] = n;
	return dp[n][k] = (r(n - 2, k - 1) + r(n - 1, k)) % mod;
}
int main()
{
	int n, k; cin >> n >> k; dp = vector<vector<int>>(n + 1, vector<int>(k + 1, -1));		
	cout << r(n, k);
	return 0;
}

코드 ( Bottom-Up )

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 3;
int main()
{
	int n, k; cin >> n >> k;
	if (k * 2 > n) {
		cout << 0;
		return 0;
	}
	vector<vector<int>> dp = vector<vector<int>>(n + 1, vector<int>(k + 1));
	for (int i = 2; i <= n; i++) {		
		dp[i][1] = i;
		for (int j = 2; j <= i / 2; j++) {
			if (j > k) continue;
			dp[i][j] = (dp[i - 1][j] + dp[i - 2][j - 1]) % mod;
		}
	}
	cout << dp[n][k];
	return 0;
}