问题描述:
为找零问题 设计一种动态规划算法:给定金额n以及各种面额d1,d2,...,dm的数量无限的硬币,求总金额等于n的硬币的最少个数,或者指出该问题无解。
(对于该问题可以用动态规划方法或者贪婪法求解,但贪婪法要注意如何说明局部最优可以保证全局最优)
思路:
各种硬币的面额为 d[1,2,3,..N],设C[i,j]为在前i种面额中求总面值为j的最优解(最少硬币数)。
初始化
C[i,j] = 正无穷 (i=0, j>=1 && j<=N)
c[i,j] = 0(i>=0 && i<=M ,j=0)
正无穷表示无解。
前i种面额中总价值为j可以分为以下几种情况:
不包含第i中面额的硬币...
包含1个第i中面额的硬币...
包含2个第i中面额的硬币...
...
包含k个第i中面额的硬币... (j-d[i]*k>=0)
如果总价值为j的金额包含k个第i种面额,而第i种硬币的面额值为d[i],则前i-1种硬币只需求凑齐金额为j-d[i]*k的最优解,
所以可以建立如下的递推关系:
C[i,j] = min(C[i-1,j-d[i]*k] + k) , 其中j-d[i]*k >=0
例子:
已知四种面额为4,2,3,5的硬币,求总金额等于7的硬币的最少个数。
很明显可以看出该问题中
两枚硬币的组合方式:
7=4+3,7=5+2,
三枚硬币的组合方式:
7=2+2+3
所以最少硬币数为2
下面利用动态规划算法求解最少硬币数。
#include <iostream> #include <iomanip> const int M = 4; const int N =7; const int KIND = 4; const int MAX_INIFINITE = 10000; int c[M+1][N+1]; using namespace std; int d[KIND + 1] = {0,4,2,3,5}; int findmoney(int i,int j); int min(int x,int y); int main() { int i; int j; for(i = 0;i<=M;i++) for(j = 0;j<=N;j++) c[i][j] = MAX_INIFINITE; for(i = 0;i<=M;i++) c[i][0] = 0; //call c[M][N] = findmoney(M,N);// if(c[M][N] < MAX_INIFINITE) cout << "总金额为" << N << ",则至少需要的硬币个数为:" << c[M][N] << endl; else cout << "No answer" << endl; for(int i = 0;i<=M;i++) { for(int j = 0;j<=N;j++) { cout << setw(8) << setiosflags(ios::left); cout << c[i][j] << " "; } cout << endl; } return 0; } int findmoney(int i,int j) { int k = 0; int value = MAX_INIFINITE; if(i + j >=2)//除去第一行和第一列 { while(j - d[i]*k >=0 && i>=1) { value = min(findmoney(i-1,j-d[i]*k)+k,value); k++;// } c[i][j] = value; } return c[i][j]; } int min(int x,int y) { return x<y?x:y; }
分析输出矩阵C[M+1][N+1]可以知道,从上图中绿色下划线标记可以知道,通过自顶向下的方式计算C[4][7]的值,仅需要知道C[1,4],C[2,2],C[2,4],C[3,2],C[3,7]
同样的。如果N = 1,则输出"No answer"
附:网上搜集的与找零问题相关的具体问题的解答:
上面两篇章文章给出的方案和算法中能求出有解的情况,并给出具体应该是哪几个面值的硬币,而且面值中含有面额为1的硬币。对无解的情况没有处理。
该文讨论的是凑齐给定金额的方式有几种。