备注:可以参考背包问题的求解方式。
一、问题描述:
有n种不同币值的硬币x1,x2,…,xn,各种硬币的可使用数量无上限,给定币值s,若存在由该n中硬币组成的找零方案则求所需最少硬币数量,否则返回-1。
二、问题分析:
动态规划求解。设f(i, j)表示利用前i种硬币获得币值j时所需的最少硬币数,则f(n, s)即为利用前n种硬币获得币值s时所需的最少硬币数。用前0种硬币获得币值0所需最少硬币数量为0,用前0中硬币不可能获得币值j(j>0),为方便计算设其为一个很大的数MAX_N。故有如下目标函数:
f(0, 0)=0;
f(0, j)=MAX_N, 1<=j<=s;
f(i, j)=min{f(i-1, j), min{f(i-1, j-k*xi)+k | 0<k*xi<=j,且f(i-1, j-k*xi)!=MAX_N}}, 1<=i<=n, 0<=j<=s;
上述分析中,时间复杂度为O(n*s*(s/min{xi})),空间复杂度O(n*s)。有f(i, j)求解过程可知f(i, j)只与f(i-1, t), 0<=t<=j有关,故只需保存f(i-1, t),而计算f(i-1, j-k*xi)+k-1实际上是在求解f(i, j-k*xi),故时间复杂度可以改进为O(n*s),空间复杂度可以改进为O(s)。
三、代码实现:
算法实现:
int min_num(int * arr,int size,int s){
assert(arr!=NULL);
const int MAX_N=0x7FFFFFFF;
int *min=new int[s+1];
if(min==NULL){
exit(0);
}
min[0]=0;
for(int i=1;i<=s;++i){
min[i]=MAX_N;
}
for(int i=0;i<size;++i){
for(int j=arr[i];j<=s;++j){
if(min[j-arr[i]]!=MAX_N&&min[j-arr[i]]+1<min[j]){
min[j]=min[j-arr[i]]+1;
}
}
}
if(min[s]!=MAX_N){
return min[s];
}else{
return -1;
}
}
测试代码:
#include<cstdlib>
#include<cassert>
#include<iostream>
int min_num(int * arr,int size,int s);
using namespace std;
int main(int argc,char*argv[]){
int arr[]={3,5,7,8,10,11,13,17,19,23,29};
int s;
int size=sizeof arr/sizeof(int);
cout<<size<<endl;
cin>>s;
cout<<min_num(arr,size,s);
}