Codeforces 37D Lesson Timetable - 组合数学 - 动态规划

题目传送门

  神奇的门I

  神奇的门II

题目大意

  有$n$组学生要上课2次课,有$m$个教室,编号为$1$到$m$。要确定有多少种不同的安排上课的教室的方案(每组学生都是本质不同的),使得它们满足:

  • 每组学生第一次上课的教室的编号小于等于第二次上课的教室的编号。
  • 第$i$间教室在第一次上课时,恰好有$x_{i}$组学生在场。
  • 第$i$间教室在某次上课时,中间包含的学生组数不能超过$y_{i}$。

  输出答案模$10^{9} + 7$。

  因为第一次上课恰好有多少人,所以这个方案数是可以直接用组合数,暂时可以扔掉。

  对于第二次上课的时候,考虑用动态规划来做,用$f[i][j]$表示,考虑到第$i$个教室,当前一共还有$j$个人没有分配教室。

  转移的时候枚举在第$i$个教室中上课的人数,再乘一乘组合数就好了。

Code

 1 /**
 2  * Codeforces
 3  * Problem#37D
 4  * Accepted
 5  * Time: 46ms
 6  * Memory: 6388k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 
11 const int N = 1005, M = 105, mod = 1e9 + 7;
12 
13 int n = 0, m;
14 int xs[M], ys[M];
15 int C[N][N];
16 int f[M][N];
17 
18 inline void init() {
19     scanf("%d", &m);
20     for (int i = 1; i <= m; i++)
21         scanf("%d", xs + i), n += xs[i];
22     for (int i = 1; i <= m; i++)
23         scanf("%d", ys + i);
24 }
25 
26 inline void solve() {
27     C[0][0] = 1;
28     for (int i = 1; i <= n; i++) {
29         C[i][0] = C[i][i] = 1;
30         for (int j = 1; j < i; j++)
31             C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
32     }
33     
34     int s = 0, ans;
35     f[0][0] = 1;
36     for (int i = 1; i <= m; i++) {
37         s += xs[i];
38         for (int j = xs[i]; j <= s; j++)
39             for (int k = 0; k <= j && k <= ys[i]; k++)
40                 f[i][j - k] = (f[i][j - k] + f[i - 1][j - xs[i]] * 1ll * C[j][k]) % mod;
41     }
42     ans = f[m][0];
43     for (int i = 1; i <= m; i++) {
44         ans = (ans * 1ll * C[n][xs[i]]) % mod;
45         n -= xs[i];
46     }
47     printf("%d\n", ans);
48 }
49 
50 int main() {
51     init();
52     solve();
53     return 0;
54 }
posted @ 2018-03-07 17:12  阿波罗2003  阅读(...)  评论(... 编辑 收藏