求解硬币问题。有1分、2分、5分、10分、50分和100分的硬币各若干枚,现在要用这些硬币支付W元,最少需要多少枚硬币?
1、我解决该问题编程的思路如下:
首先是利用一个数组A存储硬币面额,再利用另外一个数组B存储每个硬币面额对应的硬币数,利用贪心法的思想,当输入金额为W元时与最大面额的硬币作比较,若是大于面额最大的硬币则是可以取面额最大的硬币进行支付对应的数组B中的元素进行B[i]++,并且将其W-取出的硬币面额=剩下的钱,否则将其输入的W 与硬币面额次大的比较这时候也就是A数组下标后移一位(这里我是将硬币的面额进行了降序排列所以是后移一位,若是升序排列则是前移一位)判断二者大小,大的话执行B[i]++和w=w-A[i]操作,小的话执行i++操作(A数组小标后移一位),这样进行一个循环比较操作,并记录输入W元求出各硬币面额对应的个数,最后对B数组做一个求和操作,算出最少需要硬币数为多少。
2、根据题目要求实现该我问题的代码如下所示:
```java
package practical;
import java.util.Scanner;
public class Coins {
public static int CoinSum(int w) {//求返回的硬币的个数
int[] A={100,50,10,2,1};//一位数组存储面额
int[] B={0,0,0,0,0};//用来存储数组A中对应面额的硬币个数
int sum=0;
if(w<1) {
System.out.println("不存在这种情况输入不合法");
}
else{
for(int i=0;i<5;) {
if(A[i]<=w) {
B[i]++;//对应面额的硬币个数进行+1操作
w=w-A[i];//剩余钱的额度
if(w==0)
break;
}
else {//当A[i]>w的时候i++即取后一位进行比较
i++;
}
}
}
System.out.println("硬币面额:");
for(int i=0;i<5;i++){
System.out.printf(A[i]+" ");
}
System.out.println(" ");
System.out.println("各硬币面额对应的硬币数量:");
for(int j=0;j<5;j++){
System.out.printf(B[j]+" ");
}
System.out.println(" ");
for(int j=0;j<5;j++) {//对硬币个数进行求和操作
sum=sum+B[j];
}
return sum;
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("输入需要支付的金额为:");
int w=sc.nextInt();
System.out.println("输出所需硬币的个数为:"+CoinSum(w));;
sc.close();
}
}
测试结果图如下所示:
测试截图一:
测试截图二:
3、将硬币的面额都化成元来进行计算,因为题目中是说支付W元所需要的最少的硬币数量,像上面的代码都是输入的整数值,实际生活中支付的金额有可能是包含小数的,代码如下所示(与上面代码相比只是做了些许修改,将其输入的int类型改为double类型的数据,而且值得注意的是double类型的数据做加减乘除会出现精度丢失问题如何解决代码中有注释)。
Java代码如下所示:
package practical;
import java.util.Scanner;
import java.math.*;
public class Coins1 {
public static int CoinSum(double w) {//求返回的硬币的个数
double[] A={1.0,0.5,0.1,0.02,0.01};//一位数组存储面额
int[] B={0,0,0,0,0};//用来存储数组A中对应面额的硬币个数
int sum=0;
if(w<0.01) {
System.out.println("不存在这种情况输入不合法");
}
else{
for(int i=0;i<5;) {
if(A[i]<=w) {
B[i]++;//对应面额的硬币个数进行+1操作
BigDecimal p1 = new BigDecimal(Double.toString(w));//先是将double类型转换成String类型
//让后将其转换成BigDecimal类型进行计算,不然在计算的时候浮点数类型会出现精度丢失,导致运算结果有偏差
BigDecimal p2 = new BigDecimal(Double.toString(A[i]));
w=p1.subtract(p2).doubleValue();//剩余钱的额度
}
else {//当A[i]>w的时候i++即取后一位进行比较
i++;
}
}
}
System.out.println("硬币面额:");
for(int i=0;i<5;i++){
System.out.printf(A[i]+" ");
}
System.out.println(" ");
System.out.println("各硬币面额对应的硬币数量:");
for(int j=0;j<5;j++){
System.out.printf(B[j]+" ");
}
System.out.println(" ");
for(int j=0;j<5;j++) {//对硬币个数进行求和操作
sum=sum+B[j];
}
return sum;
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("输入需要支付的金额为:");
double w=sc.nextDouble();
System.out.println("输出所需硬币的个数为:"+CoinSum(w));;
sc.close();
}
}
测试截图如下所示:
测试截图一:
测试截图二:
上面这个程序值得注意的是,浮点类型数进行加减乘除操作的时候,会出现精度丢失的现象。为什么会出现这种现象的原因:主要是由于计算机中二进制无法精确的表示浮点数类型数据。在计算机中做加减乘除,最后都会将其转化成二进制进行加减乘除运算所以就会导致精度缺失问题。
所以这个时候就应该利用到java.math.中提供的一个API类,BigDecimal。
float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象
BigDecimal一共有4个构造方法:
BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用,这个仍旧出现精度丢失错误)
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象(用的较多)
像上面第二个程序中输入一个double类型的数据进行减法运算,最后出现精度丢失的错误,我开始是利用BigDecimal(double)来避免精度丢失,但是没有成功,仍旧是精度处于丢失状态,所以这时候就利用BigDecimal(String),首先是将double类型转换成String类型Double.toString(A[i]),然后就将其转换成BigDecimal类型,最后将运算的结果再装换成double类型,即p1.substract(p2).doubleValue();
转换成BigDecimal后做加减乘除形式:
p1.add(p2);//加法
p1.substract(p2);//减法
p1.mutiply(p2);//乘法
p1.divide(p2);//除法