最少找零问题与完全背包模型的一点思考

来源:互联网 发布:淘宝怎么实名认证激活 编辑:程序博客网 时间:2024/06/06 02:38


        最少找零问题描述如下,有n张硬币,价值存储在一个数组中,每种硬币个数都是无限的,再给定一个要找的零钱数,然后试用最少的数目的硬币来找我们要求的数目。

比如  5,2,3,要找出20元钱,我们人工的判断几个可能,很快就可以找出最优,也就是4张5元,也就是4.  

      如果这个题目要求用程序解决,我们可以遵循如下思路

      首先如果全部用5元,则其最多4张,如果全部用2元,则最多是10张,如果全部3元,最多6张(如果是7张,则大于20元),所以我们可以使用暴力递归来判断,在0张5元,0张2元,0张3元, 或者0张5元,0张2元,1张3元,或者0张5元,0张2元,2张3元·······

    以钱币金额为标准,其调用的数目表格如下,

    5    2   3

——————

   0     0    0

  0     0    1

  0     0    2

  0     0    3

  0    0     4

  0    0      5

  0     0     6

  0    1     0

  0    1     1

  0    1     2

 0     1     3

 0      1    4

 0     1    5      

 0     1    6

 0     2    0 

 0    2      1

 0    2     2

0     2     3

0     2     4

0     2     5      (这里3元金额的数目为5,而没有到6,是因为前面调用了0张5元,2张2元,这是剩余金额为20-2*2=16元,最多需要5张)

0     3     0

··············

从以上表格可以看到在标注的地方,选择了0张5元,2张2元,5张3元,这时候还剩1元,这个需要我们考虑,如果多了1元,该如何处理。

如果做过背包问题的多重背包问题,必然知道这个题目的模型和多重背包是很类似的,一个深度搜索,在下标到达边界时候,返回一个值,但是多重背包返回的是0,而且多重背包完全不会考虑你装完最后一个物品后, 你的背包容量还剩几个单元,是正好装满,还是差几个单元,它只关注他的总体最大价值,而这个题和完全背包问题的区别在于,这个题需要判断,我们在递归到边界的时候,“背包”的容量还有多少,如果前面选择了x个5元,y个2元,z个3元,这时候再递归到边界时候,“背包”剩余容量是0,代表前面的这些组合正好组成了我们需要的金钱数目,这时候需要返回什么?同样如果前面选择了x个5元,y个2元,z个3元,这时候再递归到边界时候,“背包”剩余容量不是0,代表前面的组合没有组成我们要的数目,还差几块钱,这时候,要返回什么?

很明显,面对第一个问题,我们只需要返回0就可以了,然后前面的硬币数目(比如 x y z)加上返回值,就可以返回其组合数。

面对第二个问题,我们可以用一个例子来看看

5   2     3

——————

···········

0   4     0 ----->  要给前面返回一个比较大的值

0   4     1 ----->  要给前面返回一个比较大的值

0   4     2 ----->  要给前面返回一个比较大的值

0   4     3 ----->  要给前面返回一个比较大的值

0   4    4 

0   5    0

0   5    1

0   5    2

上面是几组数据, 对于前四组,我们口算就可以得知其都不可以组合成20,递归到边界时候,要返回一个值,返回谁?如果是0,肯定是错误的,我们可以返回一个比较大的值,比如#define MAXSIZE 10010,返回这个MAXSIZE,往下走,在0   4   4 时候,正好可以组合成20元,返回0,然后0+4+4+0(相加顺序是从后往前的,因为递归本来就是从后往前回溯的)=8,对于0张5元,4张2元,我们可以得到5个返回值,分别是 MAXSIZE, MAXSIZE, MAXSIZE, MAXSIZE,8,我们找到这里面最小的8,就可以结束(0,4,x)这个递归了,后面也都是一样。

总结下,如果递归到边界时候,剩余硬币数目为0,也就是刚刚好,要返回0,如果是非0,则返回一个预定义的一个较大值,代码如下。

#include <iostream>#include <cstdlib>#include <cstdio>#include <cstring>using namespace std;#define MAXSIZE 10010int arr[100]={3,5};int fun(int arr[],int index,int aim){int ret=MAXSIZE;   if(index==2)   {    if(aim==0)return 0;elsereturn MAXSIZE;   }     if(aim<=0) { return 0; }     for(int i=0;i*arr[index]<=aim;i++)   {   ret=std::min(ret,i+fun(arr,index+1,aim-i*arr[index]));   } return ret;}int main(){if(MAXSIZE==fun(arr,0,2)){  cout<<0<<endl;}elsecout<<fun(arr,0,22);  return 0;}



0 0
原创粉丝点击