首页 > 代码库 > dp复习 背包[礼物]

dp复习 背包[礼物]

【问题描述】
人生赢家老王在网上认识了一个妹纸,然后妹纸的生日到了,为了表示自己的心
意,他决定送她礼物。可是她喜爱的东西特别多,然而他的钱数有限,因此他想
知道当他花一定钱数后剩余钱数无法再购买任何一件剩余物品(每种物品他最多
买一个)时有多少种方案,两种方案不同,当且仅当两种方案中至少有一件品不
同,可是由于他忙着准备泡下一个妹纸(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);
}

 

dp复习 背包[礼物]