糖果 Dp 取余Dp

由于在维护世界和平的事务中做出巨大贡献,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的倍数,并且是总数最多的选择。
二维状态,第一维是物品数量,第二维是限制。
状态表示:f[i][j]表示从前i个物品中取且总数模k为j的集合
状态属性:该条件下的最大值
状态计算;f[i][j]=max(f[i-1][j],f[i-1][((j-a[i]%k)+k)%k]+a[i]);
状态计算分为二种吧,考虑最后一步,要么取要么不取,不取的话很好说就是f[i-1][j]咯,即上一轮余数为k的值,
取的话,咳咳,我们遵循迂回打击,曲线救国的原则,我们需要找到上一轮满足条件的值,怎么找呢,算一下不就好了哈哈哈哈,假设,取之后的总数除k的余数为j,那么上一轮的某个值加上要取的值必然整除K,即(s+a[i])%k=j,其中s为上一轮的符合条件的最大值,那么有s%k==j-a[i]%k,这不就找到了么,这里j-a[i]%k是上一轮的值嗷
即取的话,状态表示为f[i-1][j-a[i]%k]+a[i],因为某种不可抗力的原因,j-a[i]%k可能是个负的,所以我们要改成f[i-1][((j-a[i]%k)+k)%k]+a[i]…
关于初值:可以发现f[0][i] ,i大于等于1且小于等于n时,这些情况都是不合理的选0个咋可能还有余数? 只有f[0][0]=0合理
这里初值的确定可以参考小呆呆大佬的总结

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,k;
int a[N];
int f[N][N];
int main(){
    cin >> n >> k;
    for(int i=1;i<=n;i++) cin >> a[i];//因为后面有-1所以最好从1开始
    for(int j=1;j<N;j++) f[0][j]=INT_MIN;
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<k;j++){
            f[i][j]=max(f[i-1][j],f[i-1][((j-a[i]%k)+k)%k]+a[i]);
        }
    }
    cout << f[n][0];
    return 0;
}

这里对f[i][j]可以从f[i-1][((j-a[i]%k)+k)%k]+a[i]为什么可以从这里转移过来做解释,(s+a[i])%k=j ,显然,这个式子里面不肯能存在负值,但你转为s%k=j-a[i]%k后就可能出现负值了 ,比如说 k=7,a[i]=5,j=3,则j-a[i]%k=-2,显然s%k不能是-2,它的值应该为2才是对的那么我们只需要(j-a[i]%k)+k)%k就可以把它变成2了
有(a+b)%k=s可以转为
a%k=((s-b%k)+k)%k
同时还有 (a+b)%k=s可以转为 (a%k+b%k)%k=s

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值