一、硬币找零问题
1.问题
有面值为1元、3元和5元的硬币若干枚,给定一个输入面额,问如何采用最少的硬币数目,得到当前面额
2.思路
找出状态转移方程,每次可以拿取1元、3元或者5元的硬币,每次拿取,硬币数加1,用d[v]表示当前面额为v的最小硬币数目,
d[0]=0; //硬币数为0,不拿
d[1]=d[1-1]+1 ; //硬币数为1,只能拿取1元的硬币,相应的当前硬币数加1
d[2]=d[2-1]+1; //硬币是为2,只能拿取1元的硬币,相应的当前硬币数加1
d[3]=d[3-1]+1 或者 d[3-3]+1; //有两种拿法,一种拿1元的,一种拿3元的,每次拿取硬币数加1,要求最小数,比较两种的硬币数目,分别为3和1,因此采取第二种拿法,d[3]=min{d[3-1]+1,d[3-3]+1}
d[5]=d[5-1]+1、d[5-3]+1、d[5-5]+1,三种拿法,d[5]=min{d[3-1]+1,d[3-3]+1,d[5]=d[5-5]+1},最小数目为1
.........以此类推
d[v]=min{d[v-1]+1,d[v-3]+1,d[v-5]+1},其中v>=0,v<0时,不算入硬币数
3.代码如下所示
public class Main {
public static int way(int d[],int n){
for (int i = 1; i <=n ; i++) {
int k1=Integer.MAX_VALUE,k3=Integer.MAX_VALUE,k5=Integer.MAX_VALUE;
if (i-1>=0) //拿取面值为1的硬币,面额减1,数量相应的加1
k1=d[i-1]+1;
if (i-3>=0) //拿取面值为3的硬币,面额减3,数量相应的加1
k3=d[i-3]+1;
if (i-5>=0) //拿取面值为5的硬币,面额减5,数量相应的加1
k5=d[i-5]+1;
//得到当前组成面额的最少硬币数
int min1=Math.min(k1,k3);
d[i]=Math.min(min1,k5);
System.out.println("面值为"+i+"最少硬币数为:"+d[i]);
}
return d[n];
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int d[]=new int[n+1];
int k=way(d,n);
System.out.println("最少硬币数量为:"+k);
}
}
4.实验结果
输入15
二、币值最大化问题
1.题目
给定一排n枚硬币,面值为正整数c1,c2,...,cn,面值可能相同,请问如何选取硬币,可以使得在其原始位置不相邻的条件下。所选币值总和最大
2.思路
c[i]为第i个硬币的面值,定义一个d[i],代表从开始位置到第i个硬币当前最大的币值总和,那么对于第i个位置的硬币,根据不相邻的选择,只有两种选择,一种是选取它,则转变为求它前i-2位置的币值最大和问题,d[i]=d[i-2]+c[i],另外一种是不选取它,则转变为求它前i-1位置的币值最大和问题,相应的d[i]=d[i-1],则状态转移方程为:
d[i]=max{d[i-2]+c[i],d[i-1]}
3.代码
public class Main {
public static int way(int c[],int n,int d[]){
if (n<2) //长度小于2的话,直接返回结果
return d[n];
d[0]=0;//第0个位置为0
d[1]=c[1]; //第一个位置就为第一个位置硬币的面值
for (int i = 2; i <=n ; i++) {
d[i]=Math.max(d[i-2]+c[i],d[i-1]); //状态转移方程
System.out.println("当前位置为"+i+"的最大硬币和为:"+d[i]);
}
return d[n];
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int c[]=new int[n+1];
int d[]=new int[n+1];
for (int i = 1; i <= n; i++) {
c[i]=sc.nextInt();
}
int max_sum=way(c,n,d);
System.out.println(max_sum);
}
}