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);
}