假设有s个面额不同的硬币: v[1] <v[2] <......<v[s]。假设v[1]=1(这一点可以保证我们总能找到一个解) 那么给定一个面额为n,用这s个硬币去凑n,写一个算法,使得所用硬币数量最小。
这个问题和0/1背包问题有些类似,唯一不同的是这里可以多次使用同一种硬币,所以子问题的结构也要做相应的调整。假设用Q(i, j)表示用1,2,...,i 前i 个硬币去凑成j 面额所需要的最少硬币的个数,那么我们很容易知道:
Q(i, j)=min {Q{i-1,j-k*v[i]}+k | 0=< k <= |j / v[i]|(表示向下取整)}初始化为Q(1,j)=j,对任意的0=<j<=n都成立,问题即求Q(s,n), 算法的时间代价为O(sn).代码如下://硬币找零问题 v表示不同硬币对应的面额,n表示要找零的总额,size表示不同类型的硬币总数
//时间O(size*n),空间O(size*n)
void coin_change(int *v,const int size,const int n){
//首先定义一个动态二维数组
int **Q=new int*[size+1];
for(int i=0;i<=size;i++){
Q[i]=new int[n+1]();
}
for(int j=0;j<=n;j++){
Q[1][j]=j; //只用面额为1的硬币找钱,
}
for(int i=2;i<=size;i++){
for(int j=0;j<=n;j++){
int q=j/v[i];
int min=INT_MAX;
for(int k=0;k<=q;k++){
if(min > Q[i-1][j-k*v[i]]+k){
min= Q[i-1][j-k*v[i]]+k;
}
}
Q[i][j]=min;
}
}
cout<<"要兑换"<<n<<",总共需要硬币:"<<Q[size][n]<<"个,分别为:"<<endl;
int i=size;
int j=n;
while(i>=1){
if(Q[i][j]<Q[i-1][j]){
int k=0;
while(Q[i][j]!=Q[i-1][j-k*v[i]]+k){
k++;
}
cout<<"面额为"<<v[i]<<"的硬币"<<k<<"个"<<endl;
j=j-k*v[i];
}else if(i==1 && j>0){
cout<<"面额为"<<v[i]<<"的硬币"<<Q[i][j]<<"个"<<endl;
}
i--;
}
delete [] Q;
}
//优化空间的超零钱问题
//时间O(size*n),空间O(n)
void coin_change_optimalSpace(int *v,const int size,const int n){
int *Q=new int[n+1]();
//这里为了trace back才建立数组T[size+1][n+1],如果只需要求出最少硬币数目,则完全不必这个值。
int **T=new int*[size+1];
//对T数组进行初始化,全0
for(int i=0;i<=size;i++){
T[i]=new int[n+1]();
}
//初始化
for(int j=0;j<=n;j++){
Q[j]=j; //初始化为只用面额为1的硬币找钱的情况
T[1][j]=j;
}
//递推求解
for(int i=2;i<=size;i++){
for(int j=v[i];j<=n;j++){
if(Q[j]>Q[j-v[i]]+1){
Q[j]=Q[j-v[i]]+1;
T[i][j]=T[i][j-v[i]]+1;
}
}
}
cout<<"要兑换"<<n<<",总共需要硬币:"<<Q[n]<<"个,分别为:"<<endl;
//打印结果
int i=size;
int j=n;
while(i>0){
if(T[i][j]!=0){
cout<<"面额为"<<v[i]<<"的硬币"<<T[i][j]<<"个"<<endl;
j-=T[i][j]*v[i];
}
i--;
}
}
http://blog.163.com/kevinlee_2010/
浙公网安备 33010602011771号