3014 -- Cake Pieces and Plates
http://acm.pku.edu.cn/JudgeOnline/problem?id=3014
整数 n, m (1 ≦ n, m ≦ 4500) が与えられて,m を n 個の整数の和に何通りに分割できるか答える問題.
ただし答えは非常に大きくなりうるので,答えを 1000000007 で割った余りを出力する.
DP で解いた.
j を i 個の整数の和に分割する方法が dp[i][j] 通りだとすると,
i == 1 のとき
j を 1 個に分割するのは (j) だけなので,dp[1][j] = 1
j == 1 のとき
1 を i 個に分割するのは (0,..,0,1) だけなので dp[i][1] = 1
i < j のとき
- j を i-1 個に分割し,それに 0 を加えることで j を i 個に分割できるので dp[i-1][j] 通り.
- i-j を j 個に分割し,それぞれの要素に 1 を加えることで dp[i-1][j-i] 通り.
よって dp[i-1][j] + dp[i][j-i] 通りになる.
例えば i = 2, j = 4 のとき,
- 4 を 1 個に分割して (4).これに 0 を加えて (0,4) の 1 通り
- 2 を 2 個に分割して (0,2), (1,1).それぞれの要素に 1 を加えて (1,3), (2,2) の 2通り
となる.
i > j のとき
j-i が整数でなくなるので,i < j の1つ目の場合だけ考えればいいので dp[i-1][j] 通り
i == j のとき
i > j の場合に加えて,さらに (1,..,1) という分割があるので dp[i-1][j] + 1 通り
あとはこれらに基づいて計算すればいい.
#include <iostream> #include <vector> using namespace std; int main() { static const int M = 1000000007; int n, m; cin >> n >> m; vector<vector<int> > dp(n, vector<int>(m, 0)); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (i == 0 || j == 0) { dp[i][j] = 1; } else if (i == j) { dp[i][j] = (dp[i-1][j] + 1) % M; } else if (i < j) { dp[i][j] = (dp[i-1][j] + dp[i][j-i-1]) % M; } else { dp[i][j] = dp[i-1][j]; } } } cout << dp[n-1][m-1] << endl; return 0; }
が,これだと TLE するので dp を int dp[4500][4500] としてグローバルに宣言しておくことで TLE を回避できた.
一度アルゴリズムがわかってしまえばコードは簡単なので C でゴルフもしてみたところ,とりあえず 168B まで縮んだ.