贪心算法题解

Posted 是馄饨呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法题解相关的知识,希望对你有一定的参考价值。

贪心算法

分配问题

力扣455 分发饼干

题目描述:

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

示例 1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:

输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

提示:

1 <= g.length <= 3 * 10[^4]

0 <= s.length <= 3 * 10[^4]
1 <= g[i], s[j] <= 2[^31] - 1

1.暴力破解法求最值

解题思路
首先我想到就是最简单的暴力破解,下面是我的思路
1.首先先排下序,这会使得比较的时候,都是最小的在前
2.找出每一个孩子能吃的最小饼干,然后将该饼干去除,我这里用的是0表示去除,然后循环遍历就行

分饼干问题肯定涉及贪心了,我这里先只列举我们最初会想到的,也就是无脑遍历

class Solution {
    public int findContentChildren(int[] g, int[] s) {
         Arrays.sort(g);
        Arrays.sort(s);
         //g[] 代表孩子  s代表饼干
        int min = Integer.MAX_VALUE;
        int count = 0;

        /*1.从最小的胃口开始找
         * 2.思路:
         * 用for循环遍历,看看是否有一个孩子满足,如果满足,count++
         * 设置一个最小值,比较最小值,如果有最小值,遍历饼干表,把最小的设置为0
         * */
        for (int item : g) {
            for (int value : s) {
                //如果满足,那么count++,饼干设置为0
                if (value >= item && value != 0) {
                    //将满足的都放入记数表
                    min = Math.min(min, value);
                }
            }
            //找出最小的去除,如果没找到,那么min还是为Integer.MAX_VALUE;如果找到,count++
            if (min != Integer.MAX_VALUE) {
                count++;
                //剔除这个饼干
                for (int j = 0; j < s.length; j++) {
                    if (s[j] == min) {
                        s[j] = 0;
                        break;
                    }
                }
            }
            min = Integer.MAX_VALUE;
        }


        return count;
    }
}

2.采用贪心策略,求最值(齐姐思路)

 public static int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int child = 0, cookie = 0;
        while (child < g.length && cookie < s.length) {
            if (g[child] <= s[cookie]) {
                ++child;
            }
            ++cookie;
        }
        return child;
    }

力扣135 分发糖果

贪心策略

题目描述:

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:

输入:[1,0,2]
输出:5
解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:[1,2,2]
输出:4
解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

解题思路
两次遍历,求分得糖果的最小值
1.先从左向右遍历一遍,如果右边孩子成绩比左边孩子的高,那么右边孩子的糖果更新为左边孩子的糖果数加一
2.从右向左遍历,如果左边孩子成绩数高于右边,并且左边糖果数小于等于右边,左边糖果更新为右边孩子的糖果数加一

至此完毕

代码

class Solution {
    public int candy(int[] ratings) {
	int length = ratings.length;
        int[] count = new int[length];
          //初始化糖果数为1
        Arrays.fill(count, 1);
        //先从左向右遍历一遍,如果右边孩子比左边的高,那么右边孩子的糖果更新为左边孩子的糖果数加一
        for (int i = 0; i < length - 1; i++) {
            if (ratings[i] < ratings[i + 1]) {
                count[i+1]=count[i]+1;
            }
        }
        //从右向左遍历,如果左边数高于右边,并且左边糖果数小于等于右边,左边糖果加一
        for (int i = ratings.length - 1; i > 0; i--) {
            if (ratings[i] < ratings[i - 1] && count[i - 1] <=count[i] ) {
                count[i - 1]=count[i]+1;
            }
        }
        int sum = 0;
        for (int c : count
        ) {
            sum += c;
        }
        return sum;
    }
}

区间问题

力扣435 无重叠区间

贪心策略

题目描述:

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

注意:

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

示例 1:

输入: [ [1,2], [2,3], [3,4], [1,3] ]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: [ [1,2], [1,2], [1,2] ]

输出: 2

解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: [ [1,2], [2,3] ]

输出: 0

解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

解题思路
首先观察题目,要找到需要移除区间的最小数量,那么结尾就很重要了,结尾越小,留给其他区间的空间越大。
1.先进行排序,对每个区间的结尾比较进行递增排序
2.区间比较:先初始化第一个区间,如果第第二个区间的头包含在第一个区间,那么就证明重叠了;如果没有,那么我们就将第一个区间的尾替换成第二个区间的尾;以此类推下去,扩大区间

代码

class Solution {
 public int eraseOverlapIntervals(int[][] intervals) {
       Arrays.sort(intervals, Comparator.comparingInt(o -> o[1]));
     //初始化第一个
     int pre=intervals[0][1];
     int sum=0;

     for (int i = 1; i <intervals.length ; i++) {
         if (intervals[i][0]<pre){
             sum++;
         }else {
             pre=intervals[i][1];
         }

     }
     return sum;
 }
}

练习

力扣 605 种花问题

题目描述

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

示例 1:

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true

示例 2:

输入:flowerbed = [1,0,0,0,1], n = 2
输出:false

提示:

1 <= flowerbed.length <= 2 * 104
flowerbed[i] 为 0 或 1
flowerbed 中不存在相邻的两朵花
0 <= n <= flowerbed.length

解题思路参考于力扣booooo_

贪心算法:从左向右遍历花坛,在可以种花的位置就种一朵,能种就种(因为在任一种花时候,不种都不会得到更优解),是一种贪心的思想。

对于当前遍历的位置,这个位置是否能种上花,必须满足以下条件:

  • 当前位置 flowerbed[i] = 0flowerbed[i]=0 + 当前这个位置是数组末尾 i == flowerbed.length - 1i==flowerbed.length−1 + 前一个位置上没有种花 flowerbed[i - 1] = 0flowerbed[i−1]=0,此时可以在该位置种花。

  • 当前位置 flowerbed[i] = 0flowerbed[i]=0 + 当前这个位置后一个位置没有种花 flowerbed[i + 1] = 0flowerbed[i+1]=0 + 当前这个位置是数组的开头 i == 0i==0,此时可以在该位置种花。

  • 当前位置 flowerbed[i] = 0flowerbed[i]=0 + 当前这个位置后一个位置没有种花 flowerbed[i + 1] = 0flowerbed[i+1]=0 + 前一个位置上没有种花 flowerbed[i - 1] = 0flowerbed[i−1]=0,此时可以在该位置种花。

  • 当前位置 flowerbed[i] = 0flowerbed[i]=0 并且只有这一个位置,即 i == flowerbed.length - 1iflowerbed.length−1 并且 i == 0i0,此时可以在该位置种花。

注意:要判断当前的位置是否是 flowerbedflowerbed 数组的边界,若是当前位置是数组的开头或结尾,此时只需要看其右边或左边的位置上是否已经种上花了(只需要看连续两个位置即可);若当前位置在数组的中间部分,此时需要看其左右两边位置上是否已经种上了花(需要看连续三个位置)。

代码:

class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {

    int m = flowerbed.length;
    
    int count = 0;
    for (int i=0;i<m;i++) {

        if (flowerbed[i] == 0 && (i == m - 1 || flowerbed[i + 1] == 0) && (i == 0 || flowerbed[i - 1] == 0)) {

            flowerbed[i] = 1;
            count++;
        }
    }
    return count >= n;
 }
}

以上是关于贪心算法题解的主要内容,如果未能解决你的问题,请参考以下文章

贪心算法:划分字母区间

算法题解之贪心法

763. 划分字母区间-贪心算法

Leetcode题解——算法思想之贪心思想

一些贪心题的题解

区间贪心算法 结合优先队列使用效果更佳——以POJ 237613283190为例