贪心算法
贪心算法(greedy algorithm ,又称贪婪算法)是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择 。
找零问题
币种:1 2 4 5 10 若干张,找零:n元。输出找零方案
思路:
(1)因为贪心是要找到最优解,所以我们要从最大的币值开始寻找
(2)每次找到符合条件的币值时,就让n减去已经找到的钱,然后继续循环,直到n不大于0时停止
import java.util.Scanner; public class Greed { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); greedy(n); } public static void greedy(int n) { int[] money = {10, 5, 4, 2, 1};//钱的面值,从大到小,因为要找最优解。 int i = 0;//表示目前用money数组中哪一个面值的钱去找零,最开始从10快开始。 int[] num = new int[money.length]; while (n > 0) {//n>0说明钱还没有找完,所以继续循环 if (n >= money[i]) {//剩余要找的钱必须大于等于当前money[i]这个面值 int zs = n / money[i]; n -= zs * money[i]; num[i]+= zs;//当前这个面值的数量加上zs } else{//如果当前面值不能找开就i++ i++; } } for (int j = 0; j < num.length; j++) {//输出 if (num[j] > 0) {//说明当前这个面值的钱使用到了,所以才能输出 System.out.printf("面值:%d 张数:%d ", money[j], num[j]); } } } }
最大的金额
假如整数n表示当前奖池中已经有的钱的总数,请你从n中删除m个数字,余下的数值对应的金额就是可以拿走的钱,我们知道人性是贪婪的,那么请帮助小明,使得余下的数字按原次序组成的新数最大。
比如当n = 92081346718538,m = 10时,则新的最大数是9888
样例输入:
92081346718538 10
1008908 5
样例输出:
9888
98
思路:
(1)先将删除问题变成保留问题,利用贪心的思想,每次在非保留的个数外寻找最大值
(2)假设输入92081346718538 10,所以我们要在14个数中删除10个,所以我们可以在前11个数中选择一个最大值,然后下次循环在从这个最大值到第12个数中寻找最大的数,直到for循环的i等于被保留的数时停止。
import java.util.Scanner; public class selectMaxNumber { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { String n = scanner.next();//输入n为String类型 int m = scanner.nextInt();//输入需要删除数的个数 if (n.length() <= m) { System.out.println("输入错误,请重新输入"); } char[] a = n.toCharArray();//将String类型的数转为char数组,为了循环判断使用 String max = "";//定义max变量存放最后早到的最大数 int retain = a.length - m;//将删除问题变成保留问题 int lastselect = -1;//存放最大数在数组中的位置,开始的时候定义为-1,在循环时在+1就从0开始了 for (int i = 1; i <= retain; i++) { char big = '0';//循环比较前先假设big为最小的值 for (int j = lastselect + 1; j < a.length - (retain - i); j++) { //这里lastselect+1主要是因为在找到一个最大的数后下次循环就从这个最大数的下一个开始 /* j < a.length - (retain - i)这个区间判断方法是 第一次循环判断的范围是j < 11,也就是0到10,因为总共有14个数,需要保留四个, 所以第一次就需要在前11个中选择一个最大的数,然后每次循环结束范围都要往后移一位 例如:92081346718538删掉十个数,第一次循环判断在92081346718中 第二次循环判断在20813467185中,第三次在134671853中,第四次就在538中 */ if (a[j] > big) { big = a[j]; lastselect = j;//找到大的数就将j赋给存放最大数地址的这个变量 } } max += big;//每次找出那个最大的数后就拼接到max中 } System.out.println(max); } } }
堆果子
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆,多多决定把所有的果子合成堆,每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有果子经过n-1次合并后,就只剩下了一堆,多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能的节省体力,假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务就是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小体力的值。
输入:第一行:一个整数n(1<=n<=100),表示果子的种类数。第二行:包含n个整数,用空格分隔。n等于0时自动退出
思路:
(1)如果n==0就停止,n==1就直接输出重量
(2)如果n>1就循环,首先将数组从小到大排序,然后每次循环都将数组的第一个数和第二个数相加,空余的位置给无限大,这样就可以循环相加了。
import java.util.Arrays; import java.util.Scanner; public class pileFruit { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt();//输入果子总共的堆数 int[] m = new int[n];//表示n堆果子重量的数组 if (n == 0) break;//n==0时直接结束 if (n == 1) { //如果只有一堆果子,就直接输出这堆果子的重量 System.out.println(scanner.nextInt()); continue; } for (int i = 0; i < n; i++) { m[i] = scanner.nextInt();//输入每堆果子的重量 } int sum = 0;//记录总共搬运的重量 for (int i = 0; i < n - 1; i++) {//循环n-1次,例如三堆果子只需要搬2次 Arrays.sort(m);//将m数组排序 sum = sum + m[0] + m[1];//先从最轻的开始合并 m[0] += m[1];//搬完后m[0]就表示合并后的重量 m[1] = Integer.MAX_VALUE;//然后m[1]给无穷大,这样每次循环就会到数组最后 System.out.println(Arrays.toString(m));//查看数组数的排序 } System.out.println(sum); } } }
可以看出输出了每次排序前的数组,也输出了最后总共消耗的体力。