问题是这样的:某个国家一共发行了a1,a2,a3,...,ak种不同面值的钞票,为了方便起见,假设a1,a2,a3,...ak依次增大。现在手上有的钱数为n,请问要如何把兑换成a1,a2,a3,...,ak这些钞票,使得所用的钞票的量为最少。这个问题看上去很简单,举一个例子,如果有1元,5元,10元3种钞票,而要兑换107元,于是就有a1=1,a2=5,a3=10,n=107。那么我们用面额最大的去除,那就是最大面额钞票的张数,比如说n/a3=107/10=10,亦即10元的10张;除过之后就有余数7,再用次大面额钞票去除,7/5=1,余数为2,所以5元钞票为1张;再把余数用第三大的面额去除,2/1=2,余数为0,于是一元的2张,没有剩下的钞票了,因此结果就是10元的7张,5元的1张,1元的2张。不过对有1元,5元,10元这个例子倒是正确,那么如果面额换为1元,3元,4元,要兑换10元的钞票呢?用上面的方法算一下,就是4元的2张,1元的2张,一共用了4张的钞票。但是若用2张3元的,1张4元的也能兑换出10元的,但却只用了3张钞票。所以看来平常的方法并不能解决“使所用钞票的量为最少”这个问题
这个问题有很多的解法,这里只想介绍利用动态规划(Dynamic Programming)和回溯的技巧来解决。
首先说说用到的数据结构,假设n是要兑换的钱数,k是能够提供的面额数目,如果要兑换100元,提供面额为1元,5元,7元的3种钞票于是有n=100,k=3。用一个数组base[]把面额先保存好,因此就有base[0]=1,base[1]=5,base[2]=7。但是这里我们假设一定提供面额为一元的钞票,否则可能有一些值兑换不出来。我们再用一个数组money[]存放兑换的钞票数,因此money[i]就是i元钱兑换出来的钞票数,由于money[0]表示0元兑换出来的钞票数,那么很自然就是0了,money[1]按照上面的假设一定是1。
其次我们说说具体的算法,假设现在要兑换i元,先看i-base[0],i-base[1],... ,i-base[k-1]是什么。例如:i=10,而面额是{1,3,4},于是这几个值就是10-1=9,10-3=7,10-4=6,换句话说,如果已经兑换好了9元7元或6元的话,要兑换十元只不过是加一张钞票而已。因此,在i-base[j]的情况下,就是多一张j元。但是,如何知道兑换i-base [j]要多少张钞票呢?别忘了money[]啊,该钞票的张数就在money[i-base[j]]中。如果已经已经兑换出i-base[j]元,用了money[i-base[j]]张钞票,于是再多加一张base[j]元就可以兑换出i元了,这样兑换i元一共用了money[i-base[j]]+1张钞票。
但是我们还没有解决题目所要求的钞票张数最少的问题,所以我们就要求出各个money[i-base[j]]+1的极小值来(j=1,2,...,k),再存在money[i]中,于是money[i]就是兑换i元的最少钞票张数了。要注意:因为i-base[j]一定小于i,所以在计算money[i]时,在i之前的值就一定要先算出来,这样money[i-base[j]]才会一个有意义的值。
说了很多,下面看看一个具体的规划的表格,还是以n=10,base[]={1,3,4}为例:
i money[i] i-base[j]
------------------------------------------------------------
0 0 --, -- , --
1 1 --, -- , --
2 2 2-1, 2-3*, 2-4*
3 1 3-1, 3-3 , 2-4*
4 1 4-1, 4-3 , 4-4
5 2 5-1, 5-3 , 5-4
6 2 6-1, 6-3 , 6-4
7 2 7-1, 7-3 , 7-4
8 2 8-1, 8-3 , 8-4
9 3 9-1, 9-3 , 9-4
10 3 10-1,10-3 ,10-4
-------------------------------------------------------------
*表示小于零,不用理会极小值,只要根据表格进行回溯就能得出结果了,看表格发现用10-3和10-4进行回溯的结果是一样的,也就是3+3+4=10和4+4+3=10的道理了,C语言的代码如下:
当然这个程序还有一个不足,那就是没有将每个面额的钞票的张数输出,还需要对程序进行一些改进,有兴趣一起研究一下吧...