礼物

2020.10.5

题目描述

\(n\) 个物品,和 \(m\) 元钱,每个物品有一个价格 \(a_i\)。现要购买一些物品(每个物品可购买一次),使得在剩下的物品中价格最低的物品也买不了。

求所有可能的方案个数。 \(1\leq n,m \leq 10^3,1\leq a_i \leq m\)

解法

考虑普通的 \(0/1\) 背包,记 \(f_{i,j}\) 表示在前 \(i\) 个数中花费 \(j\) 元所能得到的购买方案数。

但直接用这种方式计数,我们并不知道当前剩余物品的最低价格,而如果把其作为状态的话又不好转移。

换一种思路,考虑枚举这个最小值,那么枚举的这个物品就不能买,小于这个最小值的物品就必须买。那么容易想到讲物品从大到小排序,从前往后枚举这个最小值,在这个最小值位置之后的物品全部加上。用 \(m\) 减去这个和之后,再考虑用前面那些较大的物品来填充背包,答案累加上背包 \((m-suf-min,m-suf]\) 的值。

#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 1007
#define Mod 1000000007

inline bool Cmp(int x,int y){return x>y;}

long long f[N]={1};
int n,m,a[N],suf[N];
int main(){
    freopen("gift.in","r",stdin);
    freopen("gift.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n,Cmp);
    for(int i=n;i>=1;i--) suf[i]=suf[i+1]+a[i];
    long long ans=0;
    for(int i=0;i<=n;i++){
        if(i) for(int j=m;j>=a[i];j--)
            f[j]=(f[j-a[i]]+f[j])%Mod;
        if(suf[i+2]>m) continue;
        for(int j=max(m-suf[i+1]+1,0);j<=m-suf[i+2];j++)
            ans=(ans+f[j])%Mod;
    }
    printf("%lld",ans? ans:1);
}
posted @ 2020-10-05 20:51  Kreap  阅读(91)  评论(0编辑  收藏  举报