BOJ 11051 - 이항 계수 2

“이항 계수와 동적 계획”
BOJ 11051 - 이항 계수 2
문제 링크

#include <bits/stdc++.h>
using namespace std;
int dp[1001][1001];
// int solve(int n, int k)
// {
// if (k == 0 || k == n)
// return 1;
// return solve(n - 1, k - 1) + solve(n - 1, k);
// }
int solve(int n, int k)
{
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= min(i, k); j++) {
if (j == 0 || j == i)
dp[i][j] = 1;
else
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % 10007;
}
}
return dp[n][k];
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
int N, K;
cin >> N >> K;
int ans = solve(N, K);
cout << ans;
return 0;
}
이항 계수의 속성과 알고리즘은 아래 링크를 참조했다.
자연수 및 정수
(n, k)
가 주어졌을 때 이항 계수 \(\begin{pmatrix} n \\ k \end{pmatrix}\)를 구하는 법은 \(\begin{pmatrix} n - 1 \\ k \end{pmatrix} + \begin{pmatrix} n - 1 \\ k - 1 \end{pmatrix}\)으로 알려져 있는데, 이것만으로는 문제를 시간 안에 풀어낼 수가 없다. 코드에 주석이 쳐진 재귀 함수solve()
가 이걸 적용한 것인데 올리는 즉시 시간 초과가 뜨게 된다.
int solve(int n, int k)
{
if (k == 0 || k == n)
return 1;
return solve(n - 1, k - 1) + solve(n - 1, k);
}
- 알아본 결과, 이대로 제출하면 시간 복잡도가 지수함수로 뛰어서 n이 조금만 커져도 문제가 생긴다고 한다. 따라서 시간 안에 문제를 해결하기 위해 따로 배열을 만들고 이항 계수 값을 저장하면서 그 값을 가져다쓰는
동적 계획법
을 써먹어야 한다.
int dp[1001][1001];
int solve(int n, int k)
{
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= min(i, k); j++) {
if (j == 0 || j == i)
dp[i][j] = 1;
else
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % 10007;
}
}
return dp[n][k];
}
사실 뼈대는 위에 있는 재귀랑 다를 게 없다.
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j])
식이 결국 위의(n, k) = (n - 1, k - 1)(n - 1, k)
와 거의 동일하기 때문이다. 그러나 반복문에 경계 조건이 하나 생겼다.for (int j = 0; j <= min(i, k); j++)
조건문은 k가 n을 넘지 않도록 억제하면서 효과적으로 dp[][] 배열을 채울 수 있도록 해준다.문제는
(n, k)
의 값을 10007로 나눈 나머지를 출력해야 하는데, 애초에 배열에 10007을 나눈 나머지를 기록하면서 진행해야 한다. 그러지 않으면 틀리게 되는데, 아무래도 오버플로우 때문인 것 같다.