【问题描述】
人生赢家老王在网上认识了一个妹纸,然后妹纸的生日到了,为了表示自己的心
意,他决定送她礼物。可是她喜爱的东西特别多,然而他的钱数有限,因此他想
知道当他花一定钱数后剩余钱数无法再购买任何一件剩余物品(每种物品他最多
买一个)时有多少种方案,两种方案不同,当且仅当两种方案中至少有一件品不
同,可是由于他忙着准备泡下一个妹纸(chi),因此麻烦聪明的你帮帮忙。
【输入格式】
输入第一行 n 和 m, n 表示妹纸喜欢的礼物数目, m 表示现有的钱数,第二行 n
个数,表示 n 个物品的价格。
【输出格式】
输出一行一个数表示方案数目,答案对 1000000007 取模。
【 样例输入 1】
gift.in
6 25
8 9 8 7 16 5
【样例输出 1】
gift.out
15
【数据范围】
30%的数据: 0<=n<=100 0<=m<=500
100%的数据:0<=n<=1000 0<=m<=1000
注意:所有物品价格均小于 m
题解:(wfj提供的思路)
我们想如果我们设dp[i][j]表示前i个物品容量为j的方案数是多少,这个很好转,做背包就可以了,但怎么统计答案呢,我们可以先把物品从大到小排序,然后每次强制让第i个物品做我们没选的最小的数,那么后面比他小的就都要选上,前面就做一个背包就可以了,然后限制j自己推一下就看以了,最有不要忘记,如果可以全部选上也是一种方案。
代码:
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<algorithm> #include<cstring> #define ll long long const int MAXN=1010; const int mod=1000000007; using namespace std; ll dp[MAXN][MAXN],n,m,minn,w[MAXN]; ll sum[MAXN]; int cmp(int a,int b){ return a>b; } int main(){ scanf("%lld%lld",&n,&m); for ( int i=1;i<=n;++i) scanf("%lld",&w[i]); dp[0][0]=1; sort(w+1,w+n+1,cmp); for (int i=n;i;--i) sum[i]=sum[i+1]+w[i]; ll ans=0; for(int i=1;i<=n;i++){ for(int j=m-sum[i+1];j>=0&&j>m-sum[i+1]-w[i];j--) ans+=dp[i-1][j],ans%=mod; for(int j=m;j>=0;j--){ dp[i][j]=dp[i-1][j]; if(j-w[i]>=0) dp[i][j]+=dp[i-1][j-w[i]],dp[i][j]%=mod; } } if(sum[1]<=m) ans++,ans%=mod; printf("%lld\n",ans); }
时间: 2024-12-14 18:48:55