您的位置:首页 > 其它

多重集组合计数

2015-09-05 21:02 155 查看

题目

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

数据范围

1≤n≤10001 \le n \le 1000

1≤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) 时间内解决这个问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: