Openjudge 2989:糖果题解(附带一维数组优化【貌似还没有人发过这种方法】)

原题目: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个(为了方便理解我将k统一带成样例中的7)为一组的,剩余体积为还需j就能达到7的最优值

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

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

无优化动态规划

#include 
#include 
#include 
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

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

for (i=1;i<=n;i++)
   {
   	for (j=0;j

 Openjudge 2989:糖果题解(附带一维数组优化【貌似还没有人发过这种方法】)_第1张图片

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

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

最后,我们只需要输出第0列的最大值即可

一维数组优化

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

#include 
#include 
#include 
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

Openjudge 2989:糖果题解(附带一维数组优化【貌似还没有人发过这种方法】)_第2张图片

 成功Ac!!!

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

你可能感兴趣的:(算法)