一見すると計算量が厳しい問題にも見える。ちょっとした発想の転換が必要になる。
問題概要
正の整数 が与えられる。次の条件を満たす 個の整数 の組が何個あるかを 100000 で割った値を求めよ。
制約
- [tex 1 \le N \le 7]
考えたこと
安直に考えると、次のような DP をしたくなる。
dp[i][j][s]
:数列 の最初の 個について、末尾の値が () であり、総和が であるような場合の数
しかし、これだと の計算量とな理、間に合わない。ここでは発想を変えて、問題を次のように言い換えてみよう。
の中から、 個の整数を選んで、総和を にする方法が何通りあるか
これは、ごく標準的な部分和問題であり、次のような DP で解ける。
dp[v][k][s]
: の中から 個選んで、総和を にする方法の個数
の計算量となり、これならば間に合う。
コード
#include <bits/stdc++.h> using namespace std; const int MOD = 100000; int main() { int N, M, S; cin >> N >> M >> S; // 1, 2, ..., M から N^2 個選んで総和を S にする方法の数え上げ vector dp(N*N+1, vector(S+1, 0LL)); dp[0][0] = 1; for (int v = 1; v <= M; v++) { vector nex(N*N+1, vector(S+1, 0LL)); for (int k = 0; k <= N*N; k++) { for (int s = 0; s <= S; s++) { // v を選ばない nex[k][s] += dp[k][s], nex[k][s] %= MOD; // v を選ぶ if (k+1 <= N*N && s+v <= S) { nex[k+1][s+v] += dp[k][s], nex[k+1][s+v] %= MOD; } } } swap(dp, nex); } cout << dp[N*N][S] << endl; }
別解
この問題は分割数を用いることで、より高速な の計算量で解くこともできる。詳しくは次の記事を参照。