多重集组合计数

来源:互联网 发布:php有cdn获取客户端ip 编辑:程序博客网 时间:2024/05/21 09:48

题目

n 种物品,第 i 种物品有ai个。不区分同类物品,从中取出 m 个,问有多少种取法(答案取模109+7)。

数据范围

1n1000
1m1000
1ai1000

主干思路

dp[i][j] 表示前面 i 种物品,取出 j 个的取法数目。考察第 i 种物品取了 k 个(0kmin(ai,j)),那么在剩下的 i - 1 种物品中就要取出 j - k 个。
所以:

dp[i][j]=k=0min(j,ai)dp[i1][jk]

最后结果是 dp[n][m]。

优化法1

直接计算是O(nm2)的。注意到 dp 数组的第 i 行的第 j 个是前面一行的连续部分的和,我们考虑处理后缀和:

sum[i][j]=k=jmdp[i][j]

那么:
dp[i][j]sum[i][j]=sum[i1][jmin(j,ai)]sum[i1][j+1];=sum[i][j+1]+dp[i][j];

之所以要处理后缀和而不是前缀和是因为这样不用特判断边界。
另外,考察上面的规划顺序,i 循环应该是增顺序,j 循环应该是减顺序,先 i 后 j。

优化法2

同样考虑到第 i 行的第 j 个是前面一行的连续部分的和,我们应该认识到 dp[i][j] 和 dp[i][j - 1] 应该是大部分相同的。
为此我们计算:

dp[i][j]=k=0min(j,ai)dp[i1][jk]dp[i][j1]k=0min(j1,ai)dp[i1][j1k]

经过对min(...)的取值讨论之后,我们得到:
dp[i][j]dp[i][j1]={dp[i1][j]dp[i1][j1ai],dp[i1][j],j1aiotherwise

这样也可以 O(nm) 时间内解决这个问题。

0 0