HDU 4248【组合数学 + DP】

for my darling

题意:有N种石头,每种石头有A1,A2....AN个,现取出一些石头组成序列,求可以组成多少种序列

例如:3种:可以产生:B; G; M; BG; BM; GM; GB; MB; MG; BGM; BMG; GBM; GMB; MBG; MGB.

我们采用动态规划的思想,划分阶段:按照石头种类划分阶段。于是乎,咱们对于第i种石头,相当于之前石头的颜色并不重要,借助高中数学插板法的思想,假如之前的i - 1 种石头,拼出了长度为len,那么,相当于有len + 1个空,咱们要放第 i 种石头进去,于是乎,转化成了经典问题,我比较得意的总结:

球和球 盒和盒 空盒 情况数
有区别 有区别 有空盒 m^n
有区别 有区别 无空盒 M!s(n,m)
有区别 无区别 有空盒 S(n,1)+s(n,2)+…+s(n,m),n>=m
      S(n,1)+s(n,2)+…+s(n,n),n<=m
有区别 无区别 无空盒 S(n,m)
无区别 有区别 有空盒 C(n+m-1,n)
无区别 有区别 无空盒 C(n-1,m-1)
无区别 无区别 有空盒 DP
无区别 无区别 无空盒 DP

这里,第 i 种石头互相没有区别,len + 1个空有序,相当于有区别,可以有空盒,于是,如果咱们从第 i 种中放put个进去,情况数应该是 C(put + len , put)

于是设计状态:DP[i][j] 表示 用前 i 种石头,排出长度为 j 的可能数

然后,状态转移的时候,枚举在阶段 i 放入put个,DP[i + 1][j + put] += DP[i][j] * C(put + j, put) 即可

复杂度 10000 ^ 2,能过……

写时候时间较紧,外加好久没刷HDU了……出现 1:忘记判EOF 2:长度为len,有len + 1个空,忘记 + 1了……于是一直没过……

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef long long Long;
 5 
 6 const int MOD = 1000000007;
 7 
 8 int C[11111][111];
 9 
10 void init() {
11     C[0][0] = C[1][0] = C[1][1] = 1;
12     for (int i = 2; i <= 10101; i++) {
13         for (int j = 0; j <= i && j <= 100; j++) {
14             if (j == 0) C[i][j] = 1;
15             else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
16         }
17     }
18 }
19 
20 
21 int dp[111][11111];
22 int a[111];
23 
24 int main() {
25     init();
26     for (int ii = 1;;ii++) {
27         int n; if (scanf("%d",&n) == -1) break;
28         memset(dp,0,sizeof(dp));
29         memset(a,0,sizeof(a));
30         for (int i = 0; i < n; i++) {
31             scanf("%d",a + i);
32         }
33         for (int i = 0; i <= a[0]; i++) dp[0][i] = 1;
34         int maxlen = a[0];
35         for (int now = 0; now < n - 1; now ++) {
36             int next = now + 1;
37             for (int len = 0; len <= maxlen; len++) {
38                 if (dp[now][len] == 0) continue;
39                 dp[next][len] += dp[now][len];
40                 if (dp[next][len] >= MOD) dp[next][len] -= MOD;
41                 Long tmp = dp[now][len];
42                 for (int put = 1; put <= a[next]; put++) {
43                     Long tt = tmp * C[len + put][put] % MOD;
44                     dp[next][len + put] += tt;
45                     if (dp[next][len + put] >= MOD) dp[next][len + put] -= MOD;
46                 }
47             }
48             maxlen += a[next];
49         }
50         int ans = 0;
51         for (int i = 1; i <= maxlen; i++) ans = (ans + dp[n - 1][i]) % MOD;
52         printf("Case %d: %d\n",ii,ans);
53     }    
54     return 0;
55 }

 

 

posted on 2012-07-17 14:37  康某-  阅读(861)  评论(0编辑  收藏  举报

导航