多重集组合计数
2015-09-05 21:02
155 查看
题目
n 种物品,第 i 种物品有aia_i个。不区分同类物品,从中取出 m 个,问有多少种取法(答案取模109+710^9 + 7)。数据范围
1≤n≤10001 \le n \le 10001≤m≤10001 \le m \le 1000
1≤ai≤10001 \le a_i \le 1000
主干思路
dp[i][j] 表示前面 i 种物品,取出 j 个的取法数目。考察第 i 种物品取了 k 个(0≤k≤min(ai,j)0 \le k \le \min(a_i, j)),那么在剩下的 i - 1 种物品中就要取出 j - k 个。所以:
dp[i][j]=∑k=0min(j,ai)dp[i−1][j−k]
dp[i][j] = \sum\limits_{k = 0}^{\min(j, a_i)}dp[i - 1][j - k]
最后结果是 dp
[m]。
优化法1
直接计算是O(nm2)O(nm^2)的。注意到 dp 数组的第 i 行的第 j 个是前面一行的连续部分的和,我们考虑处理后缀和:sum[i][j]=∑k=jmdp[i][j]
sum[i][j] = \sum\limits_{k = j}^{m}dp[i][j]
那么:
dp[i][j]sum[i][j]=sum[i−1][j−min(j,ai)]−sum[i−1][j+1];=sum[i][j+1]+dp[i][j];
\begin{align}
dp[i][j] &= sum[i - 1][j-min(j, a_i)] - sum[i - 1][j + 1];\\
sum[i][j]&=sum[i][j + 1] + dp[i][j];
\end{align}
之所以要处理后缀和而不是前缀和是因为这样不用特判断边界。
另外,考察上面的规划顺序,i 循环应该是增顺序,j 循环应该是减顺序,先 i 后 j。
优化法2
同样考虑到第 i 行的第 j 个是前面一行的连续部分的和,我们应该认识到 dp[i][j] 和 dp[i][j - 1] 应该是大部分相同的。为此我们计算:
dp[i][j]=∑k=0min(j,ai)dp[i−1][j−k]−dp[i][j−1]−∑k=0min(j−1,ai)dp[i−1][j−1−k]
\begin{align}
dp[i][j] &- dp[i][j - 1] \\
=\sum\limits_{k = 0}^{\min(j, a_i)}dp[i - 1][j - k] &- \sum\limits_{k = 0}^{\min(j - 1, a_i)}dp[i - 1][j - 1 - k]
\end{align}
经过对min(...)min(...)的取值讨论之后,我们得到:
dp[i][j]−dp[i][j−1]={dp[i−1][j]−dp[i−1][j−1−ai],dp[i−1][j],j−1≥aiotherwise
dp[i][j] - dp[i][j - 1] =
\begin{cases}
dp[i - 1][j] - dp[i - 1][j - 1 - a_i],& j - 1 \ge a_i\\
dp[i - 1][j], &otherwise
\end{cases}
这样也可以 O(nm)O(nm) 时间内解决这个问题。
相关文章推荐
- HDU - 3277 Marriage Match III(并查集+最大流)
- WIN7 64位 IIS中ASP连接不上ACCESS数据库的解决方法
- 第0课第5节 刚接触开发板之u-boot, kernel打补丁编译
- unity内存管理
- POJ-2387-Til the Cows Come Home
- 许愿网
- React实践系列笔记-JSX
- 网络各层协议
- java下DataInputStream与DataOutputStream写入数据的同时写入数据类型
- GTONE安装时遇到的问题
- React实践笔记-Quick Start
- 刷一题Leetcode:Ugly Number
- 使用<input type="file">实现文件上传
- Direct-X学习笔记--骨骼动画进阶
- Spring MVC学习
- [LeetCode] Search a 2D Matrix
- 关于VS2010 C#使用DirectX的问题[英]
- C++技术点积累(1)
- (1.4.6.2)中位数
- 1008. 数组元素循环右移问题 (20)