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

来源:互联网 发布:java web快速开发 ide 编辑:程序博客网 时间:2024/04/29 21:52

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的范围是s[i1]<=j<s[i](同样注意是”<=”和”<”),当然s[i]>m的情况特殊判断一下

Code

// by spli#include<cstring>#include<cstdio>#include<algorithm>#include<iostream>#define LL long longusing 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 50093 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
原创粉丝点击