问题描述:
为找零问题 设计一种动态规划算法:给定金额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) , 其中k为大于等于0的整数,但需满足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的硬币。对无解的情况没有处理。
该文讨论的是凑齐给定金额的方式有几种。