当前位置:   article > 正文

贪心算法(例题详细解说)_贪心算法题目

贪心算法题目

日升时奋斗,日落时自省 

目录

1、选择排序

2、分割平衡字符串 

3、买卖股票的最佳时机 II 

4、跳跃游戏

5、钱币找零

6、多机调度问题

 7、活动选择

8、无重复区间


贪心思想:顾名思义 贪 是该算法的一大特点,如何贪???

在对于问题求解时,总会做出当前看来最好的选择,也就是说不从整体最优加以考虑;他所做出的是在某种意义上的局部最优解;

那使用什么情况?

(1)贪心算法针对所有问题的整体最优可以通过一系列局部最优的选择,即贪心选择来达到,这是贪心算法可行的第一个基本要素;

(2)当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质,运用贪心策略将每一次转化都取得最优解,每一次操作都能直接对结果产生影响

基本思路:从问题的某一初始解出发一步一步地进行,根据谋个优化测度,每一步都要确保能获得局部最优解。每一步值考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起了,就不再把数据填进部分解中,直接进行枚举,或者不再添加表示算法停止

实际上,贪心算法使用的情况比较少,再使用之前选择该问题下的几个实际数据进行分析,判断即可

算法这个问题仅仅拿文字叙述太过潦草了,通过以下可以用贪心算法试题来详细理解

1、选择排序

选择排序其实就是利用贪心算法进行的,每次找到一个最小值下标,将当前序列的最小值和当前序列的开始下标进行交换;每次排序都再影响结果,以为每次排序都涉及到当前序列的最小值,整个序列从开始位置逐渐偏向有序从小到大

 代码解析(附加注释解释):

  1. public static void selectSort(int[] arr){
  2. //外部for循环就是指当前序列 从0下标开始的当前序列 等 i++后就是 就是从1下标开始的当前序列
  3. for(int i=0;i<arr.length;i++){
  4. int minindex=i;
  5. //从当前序列开始位置后找最小值
  6. for(int j=i+1;j<arr.length;j++){
  7. if(arr[j]<arr[minindex]){
  8. minindex=j;
  9. }
  10. }
  11. //找到当前序列的最小值之后就进行交换 当前序列的开始位置就已经确定了
  12. if(minindex!=i){
  13. swap(arr,minindex,i);
  14. }
  15. }
  16. }
  17. private static void swap(int[] arr, int minindex, int i) {
  18. int tmp=arr[minindex];
  19. arr[minindex]=arr[i];
  20. arr[i]=tmp;
  21. }

2、分割平衡字符串 

题目详细了解来源力扣 :  力扣  

其实题目大体解释就是:在一个平衡字符串中: L 和 R 字符数量上是相同的,将该字符串尽可能多的分割成平衡字符串

例如: 把 LRLLLRRR 分割下来就是 LR 和 LLLRRR 这两个  既有L也有R的字符串

注:最终问的最多能分成几个平衡字符串

思路: 
(1)既然说的是平衡 那就定义一个 量表示平衡 balance 表示
(2)balance什么时候最平衡 0 的时候最平衡 
(3)如果遇到了L balance ++  ,如果遇到了R balance --  一个加一个减如果平衡不就是0
(4)最后balance 处理完了, 进行判断balance当前是不是为0 也就是表示是不是一个平衡字符串

 代码解析(附加注释解释):

  1. public int balancedStringSplit(String s){
  2. int count=0; //用来计数的
  3. int balance=0; //记录平衡
  4. for(int i=0;i<s.length();i++){
  5. //处理平衡字符串
  6. if(s.charAt(i)=='L'){
  7. balance++;
  8. }else if(s.charAt(i)=='R'){
  9. balance--;
  10. }
  11. //平衡处理结束后 进行判定是否平衡
  12. //每经过一次判定一次 也就没有回退操作,贪心算法步步都响应一系列局部最优
  13. if(balance==0){
  14. //计数 当前这段平衡字符串 是平衡的 计数加一
  15. count++;
  16. }
  17. }
  18. return count;
  19. }

3、买卖股票的最佳时机 II 

题目详细了解来源力扣 : 力扣

 给定一个数组 ,它的第i 个元素 是一支给定股票 第i天的价格

设计一个算法来计算你所能获得的最大利润,你可以尽可能地完成更多的交易(可以多次买卖一支股票)

注:手里只能有一支股票 ,也就是你买了一支股票就需要先卖掉,才能,买下一支股票

 代码解析(附加注释):

  1. public static int maxProfit(int[] prices){
  2. int result=0; //用来记录当前利润收入
  3. // 当前手上有一支股票 所以 i从 1下标开始
  4. for(int i=1 ;i<prices.length;i++){
  5. //计算今天和明天股票的利润
  6. int curProfit=prices[i]-prices[i-1];
  7. //如果股票的利润是正数那就记录下来 贪心算法 每次买卖股票最优就是有利润
  8. if(curProfit>0){
  9. result+=curProfit;
  10. }
  11. }
  12. return result;
  13. }

4、跳跃游戏

题目详细了解来源力扣 :力扣

(1)给定一个非负整数数组,你最初位于数组的第一个位置

(2)数组中的每个元素代表你在该位置可以跳跃的最大长度

(3)判断你是否能够到达最后一个位置

 代码解析(附加注释解释):

  1. public boolean canJump(int[] nums){
  2. int n=nums.length; //计算数组元素个数 用于判定跳跃是否能跳到
  3. int rightmost=0; //此处是记录 跳跃最大能跳到右侧的哪里
  4. for(int i=0;i<nums.length;i++){
  5. //当前锁在最大值不能小于当前下标了, 否则说明最大值只能跳跃到当前下标或者跳跃不到当前位置
  6. if(i<=rightmost){
  7. //贪心判定 每次都是 只保留最大跳跃的位置
  8. rightmost=Math.max(rightmost,i+nums[i]);
  9. //直到第一个最大跳跃位置能当前数组最后的位置
  10. if(rightmost<=n-1){
  11. return true;
  12. }
  13. }
  14. }
  15. return false;
  16. }

5、钱币找零

假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?
例如:将96转化为零钱 

贪心思想:

(1)从最大的币值开始 也就是 100币值 大于当前值 向后缩减

(2)进行下一个币值的比较如果小于当前值,将当前值最大处理(图片解释)

(3)最终判定96是否能全部被置换,如果不能返回NO 如果能返回最少几张钱币

代码解析(附加注释): 

  1. public static void main(String[] args) {
  2. //设置 以下面值以及对应的数量
  3. int[][] moneyCount={{1,3},{2,1},{5,4},{10,3},{20,0},{50,1},{100,10}};
  4. //输入你要 置换的面值
  5. Scanner scanner=new Scanner(System.in);
  6. int money;
  7. System.out.println("请输入要支付的钱");
  8. money=scanner.nextInt();
  9. //进行置换处理
  10. int result=solve(money,moneyCount);
  11. //判断如果返回值不是 -1 那说明能置换成功
  12. if(result!=-1){
  13. System.out.println(result);
  14. }else{
  15. System.out.println("No");
  16. }
  17. }
  18. private static int solve(int money, int[][] moneyCount) {
  19. int num=0;
  20. //贪心 思想
  21. //从面值大 钱币开始 为了能够以最少的钱币数量 置换结束
  22. for(int i=moneyCount.length-1;i>=0;i--){
  23. //求需要置换的钱 和 能中和掉多少张当前面值 取最小值
  24. //一个是 要满足 当前的钱 能 容纳下
  25. //另一个 要满足 面值数量 是否够
  26. int c=Math.min(money/moneyCount[i][0],moneyCount[i][1]);
  27. //将当前的钱 - 最大数量的面值
  28. money=money-c*moneyCount[i][0];
  29. //加上需要消耗的面值数量
  30. num+=c;
  31. }
  32. //如果不能找开 钱就还有剩余 所以返回-1
  33. if(money>0){
  34. return -1 ;
  35. }
  36. //如果能找开, 返回钱币数量
  37. return num;
  38. }

6、多机调度问题

某工厂有n个独立的作业,由m台相同的机器进行加工处理,作业i所需的加工时间ti,任何作业在被处理时不能中断,也不能进行拆分处理。现厂长请你给写一个程序:算出n个作业由m台机器加工处理的最短时间

此处:贪心思维就在于处理最大任务时间,将任务时间从降序分配给时间任务最小的机器

题目解析:

(1)首先需要输入机器数 ,一台机器处理一个任务时间

(2)如果任务数大于机器数:就是将处理最短时间任务的机器,将其他任务时间给当前机器

(3)重复以上操作(以下图解)

 代码解析(附加注释):

以下代码有点长, 图解是思路解释,先通好思路,然后看以下代码,注释详细

  1. public class MultiMachineTest {
  2. public static void main(String[] args) {
  3. int n,m;
  4. System.out.println("请输入作业数和机器数");
  5. Scanner scanner=new Scanner(System.in);
  6. //输入任务数量
  7. n=scanner.nextInt();
  8. //机器数量
  9. m=scanner.nextInt();
  10. //创建一个针对任务数量的数组 一会用于存放对应的任务的时间
  11. Integer[] works=new Integer[n];
  12. //以数组创建m个机器
  13. Integer[] machines=new Integer[m];
  14. //给每个任务都需要赋值一定的任务时间
  15. for(int i=0;i<n;i++){
  16. works[i]=scanner.nextInt();
  17. }
  18. //将所有机器处理
  19. System.out.println(greedStrateg(works,machines));
  20. }
  21. private static int greedStrateg(Integer[] works, Integer[] machines) {
  22. //首先 给定的时间肯定不是有序的 所以需要对当前传来数组进行排序 还是降序排序
  23. //贪心算法 从最大时间开始
  24. int minimaTime= machines.length; //记录最短时间
  25. int workNum=works.length; //机器数量
  26. Arrays.sort(works,new myComparator());
  27. //作业数如果小于机器数 ,直接返回最大的作业时间 其实也就是最短时间
  28. if(workNum<minimaTime){
  29. return works[0];
  30. }else{
  31. //作业数大于机器数的情况
  32. for(int i=0;i<works.length;i++){
  33. //选择最小的机器 任务时间加上
  34. int flag=0;
  35. //首先假设用第一个机器处理 ,也就是临时机器
  36. int tmp=machines[flag];
  37. //从剩余的机器中找运行时间最短的机器,进行加任下一个任务
  38. for(int j=1;j<machines.length;j++){
  39. //找最小值 进行加任务 有点像插入排序
  40. if(tmp>machines[j]){
  41. flag=j;
  42. //记录下来
  43. tmp=machines[j];
  44. }
  45. }
  46. //将当前作业交给最小的机器执行
  47. machines[flag]+=works[i];
  48. }
  49. //从所有机器中选出最后执行完成作业的机器 刚刚图解再最后找的是当前序列的最大值
  50. minimaTime=findMax(machines);
  51. return minimaTime;
  52. }
  53. }
  54. private static int findMax(Integer[] machines) {
  55. //先假设机器的 为最小值
  56. int ret=machines[0];
  57. //进行操作比对操作
  58. for(int cur:machines){
  59. //找到当前序列的最大值
  60. if(ret<cur){
  61. ret=cur;
  62. }
  63. }
  64. //返回最大值 也是所有机器运行的最短时间
  65. return ret;
  66. }
  67. }
  68. class myComparator implements Comparator<Integer> {
  69. @Override
  70. public int compare(Integer a, Integer b) {
  71. return b-a;
  72. }
  73. //按作业时间从大到小排序
  74. }

 7、活动选择

有n个需要在同一天使用同一个教室的活动a1, a2, …, an,教室同一时刻只能由一个活动使用。每个活动a[i]都有一个开始时间s[i]和结束时间f[i]。一旦被选择后,活动a[i]就占据半开时间([s[i],f[i])。如果([s[i],f[i])和([s[j],f[j])互不重叠,a[i]和a[j]两个活动就可以被安排在这一天。求使得尽量多的活动能不冲突的举行的最大数量

 代码解析(附加注释):

  1. public class ActivitySelection {
  2. public static void main(String[] args) {
  3. Scanner scanner=new Scanner(System.in);
  4. int number=scanner.nextInt();
  5. //活动 使用二维数组表示 行表示序号 列就表示0标 代表 开始时间和1标 代表 结束时间
  6. int[][] act=new int[number][2];
  7. int idx=0;
  8. for(int i=0;i<act.length;i++){
  9. act[i][0]=scanner.nextInt(); //表示开始时间
  10. act[i][1]= scanner.nextInt(); //表示结束时间
  11. }
  12. //以结束时间排序 贪心就在于找结束时间 主要就是找
  13. Arrays.sort(act,new MyComparator());
  14. //这就是 活动选择
  15. int ret=greedyActivitySelector(act);
  16. System.out.println(ret);
  17. }
  18. public static int greedyActivitySelector(int[][] act){
  19. //num表示活动个数 本身就是1个 所以num=1 i表示当前活动
  20. int num=1,i=0;
  21. //j=1表示下一个活动
  22. for(int j=1;j<act.length;j++){
  23. //表示下一个活动开始时间 大于 当前活动结束时间
  24. if(act[j][0]>=act[i][1]){
  25. //进行比对成功后 下一个活动也成了当前活动
  26. i=j;
  27. //每能进行一次比对 活动个数加一
  28. num++;
  29. }
  30. }
  31. return num;
  32. }
  33. }
  34. class MyComparator implements Comparator<int[]>{
  35. //以 活动结束时间 进行排序
  36. @Override
  37. public int compare(int[] o1, int[] o2) {
  38. return o1[1]-o2[1];
  39. }
  40. }

8、无重复区间

题目详细了解来源力扣 :力扣

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠

注意:可以认为区间的终点总是大于它的起点,区间[1,2]和[2,3]的边界相互“接触”,但没有相互重叠

 代码解释(附加注释):

  1. class MyComparatorNow implements Comparator<int[]>{
  2. @Override
  3. public int compare(int[] o1, int[] o2) {
  4. //用来计算 前者排序
  5. return o1[0]-o2[0];
  6. }
  7. }
  8. public int eraseOverlapIntervals(int[][] intervals){
  9. if(intervals.length==0){
  10. return 0;
  11. }
  12. //此处进行排序 排序的是区间的开始位置 为以下的操作做准备
  13. Arrays.sort(intervals,new MyComparatorNow());
  14. int end=intervals[0][0]; //把当前位置作为准备的区间
  15. int prev=0; //前一个区间
  16. int count=0; //用来计数 要移除的区间
  17. for(int i=1;i<intervals.length;i++){
  18. //对比的是 前一个区间的末尾位置 大于 后一个区间的初识位置
  19. //第二种情况
  20. if(intervals[prev][1]>intervals[i][0]){
  21. //当前范围内是大于后一个区间的初识位置,还要判断是否大于后一个区间的末尾位置
  22. //前一个区间包含了后一个区间,那就说明了出现重复区间了
  23. //第三种情况
  24. if(intervals[prev][1]>intervals[i][1]){
  25. //此处是包含后一个区间了 为了减少前一个大区间的影响,前一个大区间会被移除,留下后一个小区间
  26. prev=i;
  27. }
  28. //当前无论是前一个区间还是后一个区间
  29. //因为第二种情况 包含了 第三种情况 都是要进行移除数量+1
  30. count++;
  31. }else{//第一种情况
  32. //如果 前一个区间的末尾值 小于 后一个区间的初始值
  33. //那就将下一个区间作为对比区间 当前不是重叠区间
  34. prev=i;
  35. }
  36. }
  37. //返回区间个数
  38. return count;
  39. }

 总结:贪心算法针对最多最少类问题,可以进行一系列局部最优后完成整体最优,如果实际上问题,个例问题过多不就成了全部枚举,贪心算法是尽可能一系列局部最优,个别枚举。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/856729?site
推荐阅读
相关标签
  

闽ICP备14008679号