动态规划:找钱问题

  • Post author:
  • Post category:其他



问题描述:


有一个特别的国度,只发行


4


种面值的硬币,分别是



1








硬币






5








硬币






11








硬币






50








硬币





小明去售货机前买饮料,饮料售价35


元一瓶,


小明


投入了


50


元硬币。现在售货机要





15








给他。假设每种硬币的数量充足,现在要求使用最少数量的硬币,给


小明


找钱,求出这个



最少数量



是多少。



问题分析:



售货机要给


小明


找回


15


元零钱,而现在只有


4


种面值的硬币可以使用,现在的核心问题是如何使用这


4


种面值的硬币来凑齐


15


块钱,且使用的硬币数目最少。


一、要凑齐


15


元。


二、使用的硬币数目最少。


贪心算法:

每一次尽可能拿出最大的面值钞票找剩下的钱,钱数减去当前钞票的面值,如此循环直到剩下的钱数为0。此算法有例外的情况,假设如今只有3种面值的钞票,分别是1,7,10,要求找出14元钱。贪心算法得到的答案是{10,1,1,1,1},需要5张钞票,而实际情况下最佳找钱方案是{7,7},只需要两张钞票。贪心算法保证了每一步取得的是局部最优解,不能保证最终结果是最优解。


动态规划算法:



解决方案








每次只在


已知的条件





增加一个一种硬币


,使其


数额达到找钱数


,在这几种方案中选择一个


使用硬币数量最小


的一种方案







f(


i


) = n :


i





找钱数


,取值范围:


(0<=


i


<=15)




n





凑出


i





所需要的


硬币数量





n


个。


f





使硬币数量


n


最小





最佳函数




现在从


0


元钱开始找


:






0




元钱:

因为0


元根本不需要硬币,所以结果是


f(0) = 0


,作为初始化已知条件。






1




元钱:

因为只有1


元的硬币可以使用,所以可以先用


1





1


元硬币,然后再凑够


0


元即可,由于


f(0)


是我们已经算出来的,并且使用其他硬币均无法达成约束条件,所以


f(1) = 1 + f(0) = 1 + 0 = 1


注意等式


f(1) = 1 + f(0)


右边的


1


代表


1





1


元的硬币,硬币数量。






2




元钱:

因为只有1


元的硬币可以使用,所以先用


1





1


元硬币凑出


1


元钱,然后再凑够


1


元即可,由于


f(1)


是我们已经算出来的,


并且使用其他硬币均无法达成约束条件,


所以


f(2) = 1 + f(1) = 1 + 1 = 2


注意这里


f(2) = f(1) + 1


,这里加上的


1


代表


1





1


元硬币。






3


元钱



:


重复以上步骤


f(3) = 1 + f(2) = 3








4


元钱



:


重复以上步骤


f(4) = 1 + f(3) = 4








5


元钱



:


这里就有不一样的选择了,因为有


5


元硬币可以使用。


方案一:先用


1





1


元硬币,然后再凑够


4


元钱,即


1 + f(4) = 5


,注意这里的


1


代表


1





1


元的硬币;


方案二:先用


1





5


元硬币,然后再凑够


0


元钱,即


1 + f(0) = 1


,注意这里的


1


代表


1





5


元的硬币。


综合两种方案,有


f(5) = min{


1 + f(4)





1 + f(0)


} = 1








6




元钱:


方案一:先用


1





1


元硬币,然后再凑够


5


元钱,


1 + f(5) = 2;


方案二:先用


1





5


元硬币,然后再凑够


1


元钱,


1 + f(1) = 2;


综合两种方案,有


f(6) = min{


1 + f(5)





1 + f(1)


} = 2




……


省略






15




元钱:


方案一:先用


1





1


元硬币,然后再凑够


14


元钱,


1 + f(14) = 5;


方案二:先用


1





5


元硬币,然后再凑够


10


元钱,


1 + f(10) = 2;


方案三:先用


1





11


元硬币,然后再凑够


4


元钱,


1 + f(4) = 5;


综合两种方案,有


f(6) = min{


1 + f(


14


)





1 + f(1


0


),


1 + f(4)


} = 2




跟据上面的分析,要凑够


i


元,就要考虑如下各种方案的最小值


:


f(


i


) = min{ 1 + f(


i


– value[j] ) }





i


> 1





0 <= j <= num;


value[]


存储了各种面值,


value[j]


表示第


j(0<=j<num)


种面值,与其中


1


表示的面值相同。


num


表示有多少种面值。


f(0) = 0


为已知条件,因此


i > 1



#include <stdio.h>
const int num = 3; //钱币的面值数
int value[num] = {1,7,11}; //钱币的面值

void change(int n) { //找钱方法
	int money[n+1] = {0}, min, note, sum; 
	int record[n+1] = {0}, num_value[num] = {0}; //record[i]记录i块钱最后用哪张面值的钞票找出,num_value根据record计算出找钱方案
	for (int i = 1; i <= n; i++) {
		min = n;
		for (int j = 0; j < num; j++) { 
			if (i >= value[j] && min > money[i-value[j]]) {
				min = money[i-value[j]]; //在前面几个找钱方案中找出最小的值 
				note = j; //记录用了哪张钞票 
			}
		}
		money[i] = min+1; 
		record[i] = value[note];
	}
	printf("最少钱币张数: %d\n", money[n]);
	printf("找钱方案:\n");
	printf("面值	张数\n");
	sum = n;
	while (sum > 0) {//有n元钱时,最后用的钞票面值为record[n] 
		for (int i = 0; i < num; i++) 
			if (record[sum] == value[i])
				num_value[i]++;
		sum -= record[sum];
	}
	for (int i = 0; i < num; i++) {
		printf("%d	%d\n", value[i], num_value[i]);
	}
}


int main() {
	int n; //要找的钱数
	scanf("%d", &n);
	change(n);
	return 0;
}



版权声明:本文为weixin_40571331原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。