最少硬币问题
2009-09-24 14:32
127 查看
【问题描述】有n种不同面值的硬币,各硬币面值存于数组T[1:n];现用这些面值的钱来找钱;各面值的个数存在数组Num[1:n]中。
【编程任务】对于给定的1<=n<=10,硬币面值数组、各面值的个数及钱数m,0<=m<=2001,编程计算找钱m的最少硬币数。
input : 第一个数字n,后面n行每行两个数,面值T[i],面值个数Num[i];最后是钱数m。
output:最少硬币数。
Sample intput :
3
1 3
2 3
5 3
18
Sample output:
5
【分析】:一看大家都会觉得最优子结构是 dp(m)=max{dp(m-t[i])+1},但是面值的个数在哪里?不好考虑。我开始以为状态跟面值、个数、钱数三个因素有关,应该要有个三维数组来存储状态,其实不然,那样的话动态规划的优势就没啦!对动态规划的理解还在“成长阶段”呀!废话少说,切入正题!
先看状态方程,状态方程一出,程序应该就ok了!
用MianZhi[1:m]和Mian_Zhi[1:m]来表示面值为i时的最少硬币数。TV[1:N]来表示某种面值的所有钱数,即TV[i]=T[i]*Num[i];
MianZhi[i]=min{MianZhi[i-T[j]*t]+t} t表示面值为T[j]的个数
T[j]<=i<=m; 2<=j<=N; (j = 1 另行考虑,动态规划必须的起点)
当j=1时 MianZhi[i]=Mian_Zhi[i]=i/T[1]; (1<=i<=m;i%T[1]==0&&i<=TV[1])
MianZhi[0]=Mian_Zhi[0]=0;
【编程任务】对于给定的1<=n<=10,硬币面值数组、各面值的个数及钱数m,0<=m<=2001,编程计算找钱m的最少硬币数。
input : 第一个数字n,后面n行每行两个数,面值T[i],面值个数Num[i];最后是钱数m。
output:最少硬币数。
Sample intput :
3
1 3
2 3
5 3
18
Sample output:
5
【分析】:一看大家都会觉得最优子结构是 dp(m)=max{dp(m-t[i])+1},但是面值的个数在哪里?不好考虑。我开始以为状态跟面值、个数、钱数三个因素有关,应该要有个三维数组来存储状态,其实不然,那样的话动态规划的优势就没啦!对动态规划的理解还在“成长阶段”呀!废话少说,切入正题!
先看状态方程,状态方程一出,程序应该就ok了!
用MianZhi[1:m]和Mian_Zhi[1:m]来表示面值为i时的最少硬币数。TV[1:N]来表示某种面值的所有钱数,即TV[i]=T[i]*Num[i];
MianZhi[i]=min{MianZhi[i-T[j]*t]+t} t表示面值为T[j]的个数
T[j]<=i<=m; 2<=j<=N; (j = 1 另行考虑,动态规划必须的起点)
当j=1时 MianZhi[i]=Mian_Zhi[i]=i/T[1]; (1<=i<=m;i%T[1]==0&&i<=TV[1])
MianZhi[0]=Mian_Zhi[0]=0;
#include<iostream> using namespace std; int main() { int N,m,i,j; cin>>N; int *T=new int [N+1]; int *Num=new int [N+1]; int *TV=new int [N+1]; for(i=1;i<=N;i++){ cin>>T[i]>>Num[i]; TV[i]=T[i]*Num[i]; } cin>>m; int *MianZhi=new int [m+1]; int *Mian_Zhi=new int [m+1]; //先考虑第一种面值,作为动态规划的起始 for(i=0;i<=m;i++){ if(i<=TV[1]&&i%T[1]==0)//超过它所能组成的钱数肯定不行 i<=TV[1] MianZhi[i]=Mian_Zhi[i]=i/T[1]; else MianZhi[i]=Mian_Zhi[i]=0; } // MianZhi[0]=Mian_Zhi[0]=0; //在if里已有 //================================================ int tmpV,tmpNum,tmpN,k; for(i=2;i<=N;i++){ for(j=T[i];j<=m;j++){ tmpN=1;//组成某种钱数需要i面值的个数 //任一面值都只能组成大于等于它的钱数, //所以j>=T[i] while(j>=T[i]*tmpN&&tmpN<=Num[i]){//能用到面值 T[i] 的钱数 tmpV=T[i]*tmpN; if(j>tmpV&&MianZhi[j-tmpV]==0){ //剩余部分前面的面值不能组成 tmpN++; continue; } else { tmpNum=tmpN+MianZhi[j-tmpV]; if(tmpNum<MianZhi[j]||MianZhi[j]==0) //前面各种面值都扫过一遍了 //MianZhi[j]==0是说明之前没有解 Mian_Zhi[j]=tmpNum; } tmpN++; /*虽然找到了,但正如前面if所说的”剩余部分...“ 而且面值没排序,所以可能前面还有面值大的组成的最优解 所以继续++*/ } } for(k=0;k<=m;k++) MianZhi[k] = Mian_Zhi[k];//更新 } if(MianZhi[m]==0) MianZhi[m]=-1; cout<<MianZhi[m]<<endl; //===============华丽的分割线================//释放内存 delete [] T; delete [] Num; delete [] MianZhi; delete [] Mian_Zhi; delete [] TV; system("pause"); return 0; }