vlambda博客
学习文章列表

通俗易懂贪心算法一:分配问题


    

看完本文,可以顺便解决leetcode以下两个题目:

455.分发饼干(简单)

135.分发糖果(困难)

通俗易懂贪心算法一:分配问题

一、通俗易懂的贪心算法 | 思想

    贪心算法就是采用贪心的策略,保证每一次的操作都是局部最优的,从而使得结果是全局最优的。

    比如,A、B、C、都很喜欢吃橘子,A可以吃5个、B可以吃3个、C可以吃1个;但是现在只有7个橘子,问最多几个人可以吃饱;

    我们选用的贪心策略就是,吃的少的人先吃,尽量先使用量少的人吃饱,所以在这里,B、C肯定是可以吃饱的;

    在这里,又因为全局结果是局部结果的简单求和,因此,局部最优的策略同样也是全局最优的策略。

二、分配问题


455.分发饼干(简单)


题目描述

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

来源:力扣(LeetCode)

    看描述很迷,其实说白了就是:有一群孩子和一堆饼干,每个孩子有自己的饥饿度,饼干也分大小程度,一个孩子只能吃一个饼干,而且这个饼干的大小要大于孩子的饥饿度;求解最多能有多少孩子可以吃饱?


输入输出样例

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

来源:力扣(LeetCode)


题解

    根据我们局部最优的思想,当然是饥饿度小的孩子先吃了,毕竟饼干只有这么多,但是同时应该尽量使得剩下的饼干大小能够满足后面的孩子去吃;

    因此,我们应该在饼干大小 >= 这个孩子饥饿度的饼干中,寻找到最小的那个饼干去喂他;满足一个孩子吃饱之后,继续使用同样的方法去喂饱下一个孩子,知道不能满足条件为止;

    简而言之就是,给饥饿度小的孩子分配满足条件的饼干中大小最小的饼干。

class Solution {public: int findContentChildren(vector<int>& g, vector<int>& s) {      //贪心算法 先排序  然后依次比较 sort(g.begin(),g.end()); //g 胃口 s 饼干大小 sort(s.begin(),s.end()); int i = 0,j = 0;      while(i < g.size() && j < s.size()) {          if(g[i] <= s[j]) { i++; } j++; }      return i; }};

135.分发糖果(困难)


题目描述

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:每个孩子至少分配到 1 个糖果。评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。那么这样下来,老师至少需要准备多少颗糖果呢?

来源:力扣(LeetCode)

    我们继续来把题目描述的通俗一些:有评分属性的孩子,排成一排,每一个孩子至少有一个糖果,如果一个孩子比左右两个孩子评分高,则他的糖果肯定要比左右两位的高


输入输出样例

输入:[1,0,2]

输出:5

解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。


题解

    上述455题,我们首先对于数据进行了排序,这是数据处理的常规操作,为了方便后续的大小比较;

    但是此题我们使用两次遍历;我们的贪心策略是:每一次遍历只考虑相邻一侧的关系;

    1)每一个孩子的糖果初始化 1

    2)从左到右遍历一次;如果右边孩子比左边孩子评分高,那么右边孩子得到的糖果比左边的多一个;

    3)从右到左遍历一次;如果左边孩子比右边孩子评分高,而且当前左边孩子的糖果是比右边孩子糖果少,那么左边孩子得到的糖果比右边的多一个;

class Solution {public: int candy(vector<int>& ratings) { int size = ratings.size(); if (size < 2) { return size; } vector<int> num(size,1); // 先都给一个糖果
for (int i = 1; i < size; ++i ) { // 从左到右 if (ratings[i] > ratings[i-1]) { num[i] = num[i-1] + 1; } }
for (int i = size-1; i > 0; --i ) { // 从右到左 if (ratings[i] < ratings[i-1]) { num[i-1] = max(num[i-1],num[i] + 1); } }        return accumulate(num.begin(),num.end(),0); }};