[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))

原文地址:https://www.cnblogs.com/Foggy-Forest/p/13719675.html