[题目概述]
由于在维护世界和平的事务中做出巨大贡献,Dzx被赠予糖果公司2010年5月23日当天无限量糖果免费优惠券。
在这一天,Dzx可以从糖果公司的 N 件产品中任意选择若干件带回家享用。
糖果公司的 N 件产品每件都包含数量不同的糖果。
Dzx希望他选择的产品包含的糖果总数是 K 的整数倍,这样他才能平均地将糖果分给帮助他维护世界和平的伙伴们。
当然,在满足这一条件的基础上,糖果总数越多越好。
Dzx最多能带走多少糖果呢?
注意:Dzx只能将糖果公司的产品整件带走。
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行 1 个整数,表示糖果公司该件产品中包含的糖果数目,不超过 1000000。
输出格式
符合要求的最多能达到的糖果总数,如果不能达到 K 的倍数这一要求,输出 0。
数据范围
1 ≤ N ≤ 100,
1 ≤ K ≤ 100,
输入样例:
5 7
1
2
3
4
5
输出样例:
14
样例解释
Dzx的选择是2+3+4+5=14,这样糖果总数是7的倍数,并且是总数最多的选择。
题意就是让我们从n个物品中选任意个使得权值和最大,并且是k的倍数,01背包就是解决这种选择问题
状态表示:f[i][j]
********集合:所有只考虑前i个数,切总和对k的余数为j的所有方案
********属性:最大值
状态计算:分为两个部分,不选第i个数,选第i个数
********不选:f[i -1][j]
********选:f[i - 1][(j - a[i]) % k + a[i]
这个题和波动数列的证明过程是相同的,都是同余定理,此处就不做详细解释,不同的细节之处在代码中解释。
- 完整代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int a[N], f[N][N], n, k;
int main(){
cin >> n >> k;
for(int i = 1; i <= n ; i ++){
cin >> a[i];
}
// 将所有数初始化为无穷小(-1e6以下),这两种方法都可以,但是f[0][0]要初始化为1
// 因为f[0][1], f[0][2]等都没有意义,不选数它的余数不可能大于0
memset(f, -0x3f, sizeof f);
// for(int i = 0; i < N; i ++)
// for(int j = 0; j < N; j ++)
// f[i][j] = -1e8;
f[0][0] = 0;
for(int i = 1; i <= n; i ++){
for(int j = 0; j < k; j ++){
// 第二种()中的内容看起来很复杂,(j + k - a[i] % k)这个+k再%k
// 是为了使()中的内容非负,其他就是和上面分析的一样了
f[i][j] = max(f[i - 1][j], f[i - 1][(j + k - a[i] % k) % k] + a[i]);
}
}
// 我们要求的是k的倍数,所以余数是0
cout << f[n][0];
return 0;
}
本题的分享就结束了, 也是01背包问题的应用,就是代码的细节不容易想,有问题的小伙伴可以留言
别忘了点赞关注加收藏!
文章介绍了如何用01背包算法解决Dzx在维护世界和平中获取糖果的问题,涉及状态转移方程和代码实现,展示了IT技术在解决实际问题中的应用。
765

被折叠的 条评论
为什么被折叠?



