[NOIp模拟赛][01背包] 礼物
题面
小 \(T\) 要给他的妹妹买礼物。他会不断的买,直到自己身上没有足够的钱来买任何一件礼物为止,他想知道有多少种方案符合他买礼物的方式。
我们认为两种选择方案不同当且仅当它们选取的的物品的集合不同。
因为答案可能很大,你只需要输出答案对 \(10^7+7\) 取模的结果即可。
共有 \(n\) 件礼物,价格为 \(C_i\),小 \(T\) 持有的钱数为 \(m\)。
对于 \(100%\) 的数据,满足 \(1 \le n \le 1000\),\(1 \le m \le 1000\),\(0 \le C_i \le m\)。
这样的统计方案题,如果抛开“必须买到没有钱”这个限制,是可以用 \(01\) 背包统计方案的:
\(f_i = \sum f_{i-C_j},\ j \in \{x | i - x \ge 0\}\)
沿着这个思路想下去,我们可以考虑仍然用这种方式统计方案,然后将对答案有贡献的方案计入答案。
我们可枚举现在没有选择的价值最小的物品 \(i\),再枚举一个剩余的钱数 \(j\),满足 \(j \lt C_i\),这样的答案便是合法的。
对于 \(i\) 的枚举,直接排序一遍便可以顺序扫描了。
代码:
# include <iostream>
# include <cstdio>
# include <algorithm>
# define MAXN 1005
# define MAXC 1005
const int MOD = 1e7+7;
int co[MAXN], f[MAXC];
int main(){
int n, m;
scanf("%d%d", &n, &m);
int sum = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &co[i]);
sum += co[i];
}
std::sort(co+1, co+n+1);
int ans = 0; f[0] = 1;
if(m >= sum){ // 特判
ans = 1;
}
else{
for(int i = n; i >= 1; i--){
sum -= co[i];
for(int j = std::max(0, (m-sum)-(co[i]-1)); j <= m-sum; j++){
ans = (ans += f[j]) % MOD;
}
for(int j = m; j >= co[i]; j--){
f[j] = (f[j] + f[j-co[i]]) % MOD;
}
}
}
printf("%d", ans);
return 0;
}
复杂度:\(O(nm)\)