信息学奥赛一本通 1299 / Openjudge 2989:糖果题解(附带一维数组优化【貌似还没有人发过这种方法】)

本文介绍了如何使用动态规划和背包算法解决糖果公司的问题,即找到在满足糖果总数为特定倍数的条件下,Dzx能获取的最大糖果数。通过一维数组优化,减少了空间复杂度,成功求解了问题。
摘要由CSDN通过智能技术生成

原题目:OpenJudge - 2989:糖果

前言:这是我最开始学习动态规划的一道题,在写完这道题后就被这种算法深深吸引,在我对着代码独自悟了1小时后,我就体会到动规的奇妙之处

描述

由于在维护世界和平的事务中做出巨大贡献,Dzx被赠予糖果公司2010年5月23日当天无限量糖果免费优惠券。在这一天,Dzx可以从糖果公司的N件产品中任意选择若干件带回家享用。糖果公司的N件产品每件都包含数量不同的糖果。Dzx希望他选择的产品包含的糖果总数是K的整数倍,这样他才能平均地将糖果分给帮助他维护世界和平的伙伴们。当然,在满足这一条件的基础上,糖果总数越多越好。Dzx最多能带走多少糖果呢?
注意:Dzx只能将糖果公司的产品整件带走。

输入

第一行包含两个整数N(1<=N<=100)和K(1<=K<=100)
以下N行每行1个整数,表示糖果公司该件产品中包含的糖果数目,不超过1000000

输出

符合要求的最多能达到的糖果总数,如果不能达到K的倍数这一要求,输出0

样例输入

5 7

1

2

3

4

5

样例输出 

14

Dzx的选择是2+3+4+5=14,这样糖果总数是7的倍数,并且是总数最多的选择。

在这里我用a[][]数组表示动态规划数组(a[i][j]表示前i个数组合出,

拿出7个(为了方便理解我将 统一带成样例中的7)为一组的,剩余体积为j就能达到7的最优值

根据背包算法(可以看以往的题解)的思路,我们发现这道题与以往不同,所求的恰好为7的倍数,也就是说背包的最大的体积为7(超过7的可以视作无效,如8可以看作拿出1组7后剩下1),其余的数表示为i%k,接着再一层一层递推,最后输出

为了方便理解,我先将最初的代码写出

无优化动态规划

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,k,sum,ans;
int p[100005],a[105][105];
int main()
{
	int i,j; 
    cin>>n>>k;
    for(i=1;i<=n;i++)
	{
		scanf("%d",&p[i]);
	}
    memset(a,-127,sizeof(a));
    for(i=0;i<=n;i++)
	{
		a[i][0]=0;
	}
    for(i=1;i<=n;i++)
	{
        for(j=0;j<k;j++)
		{
            a[i][j]=max(a[i-1][(k-(p[i]%k)+j)%k]+p[i],a[i-1][j]);
        }
    }
    printf("%d",a[n][0]); 
    return 0;
}

没有怎么理解?将传递值附上

for (i=1;i<=n;i++)
   {
   	for (j=0;j<k;j++)
   	{
    	cout<<a[i][j]<<" ";
	}
	cout<<endl;
}
测试代码如上

 

我将无法取到的值统一赋值为无穷小( memset(a,-127,sizeof(a)); ),避免0向下传递影响结果,但a[i][0]必须赋值为0,不然所有值将任然为 -∞ + p[i] = -∞

接着分析,每一行我们都是将余数相加进行运算((k-(p[i]%k)+j)%k),如果大于7就将它分组,再将剩余的数x排到这一行 j = x的位置,在之前的基础上加上第i个糖果的数量(!!!注意:列j(上方的图竖着看的位置)才是我们要求的余数,不要被数值影响了思路) 

我们将余数为j(位置)的数加上当前糖果的数量,就是(p[i] + j)%7,也就是

a[i][(p[i] + j)%7] = p[i] + a[i-1][j]

我们将式子转换一下,用j表示p[i] +j得到 

a[i][j] =p[i] + a[i-1][7-(p[i]%7)+j)%7)](%7是为了防止p[i]过大导致7-p[i]为负数和将余数保留

再与原来保存的值作比较,判断最优的值保存

最后,我们只需要输出第0列(也就是余数为0,没有余数)的最大值即可

一维数组优化

由上方代码可以看出,每i行只与第i-1行所保存的最优值有关,而与其余层无关,那么我们是否可以想到,只运用两行的数组(可以类似看作两个一维数组)第一行运用第二行的数据递推,第二行运用第一行的数据递推,这种方法大大节省了空间

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,k,sum,ans;
int p[100005],a[2][105];
int main()
{
	int i,j; 
    cin>>n>>k;
    for(i=1;i<=n;i++)
	{
		scanf("%d",&p[i]);
	}
    memset(a,-127,sizeof(a));
	a[0][0]=0;
	a[1][0]=0;
    for(i=1;i<=n;i++)
	{
        for(j=0;j<k;j++)
		{
            a[i%2][j]=max(a[(i+1)%2][(k-(p[i]%k)+j)%k]+p[i],a[(i+1)%2][j]);
        }
    }
    printf("%d",max(a[0][0],a[1][0])); 
    return 0;
}

 成功Ac!!!

制作不易请多多点赞!!!

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值