双“11”的抉择
题目描述
把钱花完了,所以单身了,单身了所以过双“11”,过双“11”所以把钱花完了。
今年Nova君(三号)照旧过着他暗无天日的“买买买”的双“11”,然而因为囊中羞涩,并不能够太任性。他的购物车中,列满了数不清的商品,共有N件,好多商品居然还不止一件 __(:3 」∠)_ 现在Nova君要做出一个艰难的抉择,他要从所有商品中挑出m件拼成一个订单,请问有多少种凑单的方法呢?求访法数对M的余数。
PS:同一种商品不作区分。
输入
多组测试数据(不超过100组)
每组数据两行,第一行为三个正整数N,m,M,具体意义详见描述,第二行为N个正整数a1,a2,,,an,代表第i个商品的个数
(1<=N,ai,m<=1000,2<=M<=10000)
输出
对于每组数据,输出一行,表示方法总数
输入样例
3 3 10000
1 2 3
输出样例
6
解题思路:
关键词:多重集组合数
1、为了拉不重复计算,最好是同一种物品一次性解决掉,所以定义如下:
dp[i+1][j] : 从前 i+1 种物品中取出 j 个的方案总数
2、状态转移方程:
前 i+1 种物品取出 j 个 = 前 i+1 种物品取出 j-1个 + 前 i 种物品取出 j 个 - 前i种物品中取出 j-1-ai 个.
因为取出 j-1-ai个, 最后需要 j-1个, 则 ai 需要全部取出, 前两个相重复, 则必然全部取出.
递推公式: dp[ i+1 ][ j ] = dp[ i+1 ][ j-1 ] + dp[ i ][ j ] - dp[ i ][ j-1-ai ] 时间复杂度O(nm).
呈上代码:
#include <bits/stdc++.h> using namespace std; const int N=1010; int a[N]; int b[N][N]; int main() { int n,m,M; while(scanf("%d%d%d",&n,&m,&M)==3) { for(int i = 0; i < n; i++) scanf("%d",&a[i]); for(int i = 0; i <= n; i++) b[i][0] = 1; for(int i = 0; i < n; i++) for(int j = 1; j <= m; j++) { if(j -1 - a[i] >= 0) b[i+1][j] = (b[i][j] + b[i+1][j-1] - b[i][j-1-a[i]] +M)%M; else b[i+1][j] = (b[i][j] + b[i+1][j-1])%M; } printf("%d\n",b[n][m]); } }
作者: 伊甸一点
出处: http://www.cnblogs.com/zpfbuaa/
本文版权归作者伊甸一点所有,欢迎转载和商用(须保留此段声明),且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
原文链接 如有问题, 可邮件(zpflyfe@163.com)咨询.