礼物

2020.10.5

题目描述

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

求所有可能的方案个数。 (1leq n,m leq 10^3,1leq 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);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/13771512.html