题目
n 种物品,第 i 种物品有 ai 个。不区分同类物品,从中取出 m 个,问有多少种取法(答案取模 109+7 )。
数据范围
1≤n≤1000
1≤m≤1000
1≤ai≤1000
主干思路
dp[i][j] 表示前面 i 种物品,取出 j 个的取法数目。考察第 i 种物品取了 k 个(
0≤k≤min(ai,j)
),那么在剩下的 i - 1 种物品中就要取出 j - k 个。
所以:
dp[i][j]=∑k=0min(j,ai)dp[i−1][j−k]
最后结果是 dp[n][m]。
优化法1
直接计算是
O(nm2)
的。注意到 dp 数组的第 i 行的第 j 个是前面一行的连续部分的和,我们考虑处理后缀和:
sum[i][j]=∑k=jmdp[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];
之所以要处理后缀和而不是前缀和是因为这样不用特判断边界。
另外,考察上面的规划顺序,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]
经过对 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
这样也可以 O(nm) 时间内解决这个问题。