整币兑零
来源:互联网 发布:爱知产业大学 推荐 编辑:程序博客网 时间:2024/06/05 01:13
整币兑零是一个特殊的分解统计案例,其不同的的兑换种类与零币的种类及各零币的具体数值密切相关;
本节探讨特定的6种零币与一般从键盘输入的m种零币的两类兑零统计;
特定整币兑零
把一张1元整币兑换成1分,2分,5分,1角,2角和5角共6种零币,共有多少种不同兑换种数?
一般地,把一张二元整币,5元整币或一张n元整币兑换成1分,2分,5分,1角,2角和5角共6种零币,共有多少种不同兑换种数?
1.说明:
一般地设整币的面值为n个单位,面值为1、2、5、10、20、50单元零币的个数分别为p1、p2、p3、p4、p5、p6;
显然需要解一次不定方程:
- p1+2*p2+5*p3+10*p4+20*p5+50*p6=n
其中p1、p2、p3、p4、p5、p6为非负整数;
对这六个变量实施枚举,确定枚举范围为:
0<=p1<=n,0<=p2<=n/2,0<=p3<=n/5;
0<=p4<=n/10,0<=p5<=n/20,**0<=p6<=n/50
**;
在以上枚举的6重循环中,若满足条件p1+2*p2+5*p3+10*p4+20*p5+50*p6=n,则为一种兑零方法,输出结果并通过变量m统计不同的兑换种类;
2.程序设计:
#include<stdio.h>#include<math.h>int main(){ int p1,p2,p3,p4,p5,p6,n; long m; printf("请输入整币量n:"); scanf("1分 2分 5分 1角 2角 5角 \n"); for(p1=0;p1<=n;p1++) for(p2=0;p2<=n/2;p2++) for(p3=0;p3<=n/5;p3++) for(p4=0;p4<=n/10;p4++) for(p5=0;p5<=n/20;p5++) for(p6=0;p6<=n/50;p6++) if(p1+2*p2+5*p3+10*p4+20*p5+50*p6==n) /*根据条件检验*/ { m++; printf("%5d%5d%5d",p1,p2,p3); printf("%5d%5d%5d\n",p4,p5,p6); } printf("%d(1,2,5,10,20,50)=%ld \n",n,m);}
3.程序运行示例及其注意事项:
请输入整币量n:1001分 2分 5分 1角 2角 5角0 0 0 0 0 20 0 0 0 5 00 0 0 1 2 1......100(1,2,5,10,20,50)=4562共有4562个解,即有4562种不同的兑换种数。
注意:当输入数值过大时,兑换种数相应越多,时间也就越长;
精简枚举循环设计
1.说明:
在上述程序的6重循环中,我们可精简p1循环,在循环内应用:
- p1=n-(2*p2+5*p3+10*p4+20*p5+50*p6)
p1赋值,如果p1为非负数,对应一种兑换法;
当n较大时程序运行时间较长,主要是每一个解都要打印,如果只需要计算兑换种数,则可省略打印语句,这样可大大缩减程序的运行时间;
2.程序设计:
#include<stdio.h>#include<math.h>int main(){ int p1,p2,p3,p4,p5,p6,n; long m; printf("请输入整币量n:"); scanf("%d",&n); for(p2=0;p2<=n/2;p2++) /*已省略p1循环*/ for(p3=0;p3<=n/5;p3++) for(p4=0;p4<=n/10;p4++) for(p5=0;p5<=n/20;p5++) for(p6=0;p6<=n/50;p6++) { p1=n-(2*p2+5*p3+10*p4+20*p5+50*p6); /*p1为一分币的个数*/ if(p1>=0) m++; /*用m统计兑换种数*/ } printf("%d(1,2,5,10,20,50)=%ld \n",n,m);}
3.程序运行示例及其注意事项:
请输入整币量n:200200(1,2,5,10,20,50)=69118
进一步优化枚举设计
1.说明:
以上程序的循环次数已经大大精简,进一步分析,可以看到在程序的循环设计设置中p3循环可从0~n/5改进为0~(n-2*p2)/5,因为在n中p2已占去了2*p2,以此类推,对p4、p5、p6的循环可作类似的循环参量优化;
2.程序设计:
#include<stdio.h>int main(){ int p1,p2,p3,p4,p5,p6,n; long m=0; printf("请输入整币量n:"); scanf("%d",&n); for(p2=0;p2<=n/2;p2++) for(p3=0;p3<=(n-2*p2)/5;p3++) for(p4=0;p4<=(n-2*p2-5*p3)/10;p4++) for(p5=0;p5<=(n-2*p2-5*p3-10*p4)/20;p5++) for(p6=0;p6<=(n-2*p2-5*p3-10*p4-20*p5)/50;p6++) { p1=n-(2*p2+5*p3+10*p4+20*p5+50*p6); if(p1>=0) m++; /*用m统计兑换种数*/ } printf("%d(1,2,5,10,20,50)=%ld \n",n,m);}
3.程序运行示例及其注意事项:
请输入整币量n:500500(1,2,5,10,20,50)=3937256
注意:以上3个设计尽管都是枚举,但循环的设置与循环参量的改进可精简去一些不必要的比较操作,可大大缩减程序的运行时间;
一般整币兑零
把整币兑零的零币一般化为m种,每一种零币值从键盘输入;
1.说明:
因为零币的种类较多时,应用枚举显然不能胜任,考虑应用递推求解,应用递推求解的关键在于寻求递推关系;
设整币为n个单位,m种指定零币从小到大分别为x1,x2,……,xm个单位,整币兑零实际上是一个整体数无序可重复化零问题;
记a(j,i)为整体数是i,最大零数是xj的化零种数,当去掉一个xj后,整体数变为p=i-xj,最大零数可为x1,或x2,……,或xj(因为可重复),于是有递推式:
- a(j,i)=a(1,p)+a(2,p)+……+a(j,p) (其中p=i-xj)
可据整体数i能否被x1整除确定初始条件:
a(1,i)=1 (当i能被x1整除时)
a(1,j)=0 (当i不能被x1整除时)
作以上函数递推,分别计算得a(1,n),a(2,n),……,a(m,n),求和即得所求的整币兑零种数:
- n(x1,x2,……,xm)=a(1,n)+a(2,n)+……+a(m,n)
应用函数递推简化了化零的难度;
2.程序设计:
#include<stdio.h>int main(){ int p,i,j,n,m,k; static int x[12]; static long int a[12][1001]; long b,s; printf("请输入整币值(单位数):"); /*输入处理数据*/ scanf("%d",&n); printf("请输入零币种数:"); scanf("%d",&m); printf("(从小到大依次输入每种零币值)\n"); for(i=1;i<=m;i++) { printf("第%d种零币值(单位数):",i); scanf("%d",&x[i]); } for(i=0;i<=n;i++) /*确定初始条件*/ if(i%x[1]==0) a[1][i]=1; else a[1][i]=0; for(s=a[1][n],j=2;j<=m;j++) /*递推计算a(2,n),a(3,n),...*/ { for(i=x[j];j<=n;i++) { p=i-x[j]; b=0; for(k=1;k<=j;k++) b+=a[k][p]; a[j][i]=b; } s+=a[j][n]; /*累加a(1,n),a(2,n),...*/ } printf("整币兑零种数为:%ld\n",s); /*输出兑零种数*/}
3.程序运行示例及其注意事项:
请输入整币值(单位数):1000请输入零币种数:9(从小到大依次输入每种零币值)第1种零币值(单位数):1第2种零币值(单位数):2第3种零币值(单位数):5第4种零币值(单位数):10第5种零币值(单位数):20第6种零币值(单位数):50第7种零币值(单位数):100第8种零币值(单位数):200第9种零币值(单位数):500整币兑零种数为:327631321
这一问题如果应用前面的枚举设计求解,显然难以胜任;
注意:本程序是求解整币兑零,事实上输入的整币值并不限于实际的100、500、200、1000等,可输入234、5017等任意整数“整币值”,输入的零币值也不受实际约束,只要小于整币值的任意整数即可;
- 整币兑零
- 整币兑零问题
- 整币兑零问题
- matlab的图像操作——输出图像尺寸大小、坐标轴等各项设置
- JavaScript基础篇章(总结3)
- c++中的string常用函数用法总结
- WINDOWS命令和批处理
- MySQL存储过程详解 mysql 存储过程
- 整币兑零
- 把所有的艰难全都抛诸脑后,致2016年的自己
- android中的PopupWindow的使用
- Matlab中将多维数组转换为一维数组
- JAVA中调用变量值的小技巧
- 【Unity教程】UGUI中如何动态生成精灵的几种方法
- Mac环境下载编译Android源码方法记录
- Spring常用注解(一)
- 标准论文参考文献添加方法——Zotero入门使用教程