(noip 模拟 gift)<丧病背包>

9 篇文章 0 订阅
3 篇文章 0 订阅

Problem

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

Solution

  • 这个题最重要的是理解f[][]的含义:f[i][j]表示对n~i的物品做背包之后,剩余容量为j的方案数
  • 那么首先对物品的价格从小到大排序,排序的目的放在后面
  • s[i]表示从1到i的前缀和
  • DP是倒着循环的,从价格高的物品开始,即 f[i][j]+=f[i+1][j+v[i]] ,初值为 f[n+1][m]=1 ,具体意义和01背包相同
  • 更新答案的时候,可以假设前i-1种物品一共花费了X= i1k=1ak 元,分配给前i-1种物品的容量为X时的方案数一定为1,而求的是买不到时的方案数,因此分配给第i个物品的钱数c应该满足 0<=c<ai (注意是”<=”和”<”)。根据乘法原理,剩余j的容量时,买不到第i个物品的方案数应该为 1×f[i+1][j] ,而j的范围是 s[i1]<=j<s[i] (同样注意是”<=”和”<”),当然s[i]>m的情况特殊判断一下

Code

// by spli
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
#define LL long long
using namespace std;

const int N=1010;
const int mod=1e9+7;
int n,m;
int a[N];
LL s[N];
LL f[N][N];
LL ans;

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;++i) s[i]=s[i-1]+a[i];
    if(s[n]<m){     //特判
        cout<<1;return 0;
    }
    f[n+1][m]=1;
    for(int i=n;i>=1;--i){
        int k=min((LL)m+1,s[i]);
        for(int j=s[i-1];j<k;++j) (ans+=f[i+1][j])%=mod;
        for(int j=m;j>=0;--j){
            if(j+a[i]<=m) (f[i][j]+=f[i+1][j+a[i]])%=mod;
            f[i][j]+=f[i+1][j];
        }
    }
    cout<<ans;
    return 0;
}




/*
100 500
93 394 335 69 393 420 148 389 
126 435 42 51 266 256 290 136 
301 41 120 110 475 178 500 240 
409 66 212 499 442 27 372 456 
328 372 210 20 227 142 274 468 
185 337 20 164 382 321 481 182 
218 292 216 155 293 462 322 398 
239 94 188 384 184 270 199 223 
77 454 363 197 152 165 1 431 
308 166 19 403 97 213 350 306 
318 428 310 478 251 383 27 
264 489 193 152 165 268 53 
323 79 69 221 480 277 
*/

// 答案为318465
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值