刷题笔记NO.2--贪心算法之分发糖果

文章描述了一道LeetCode题目,如何按照评分高低给n个孩子分发糖果,保证每个孩子至少一个,相邻评分高的孩子得到更多。作者提供了两种高效算法:一次左到右遍历和两次遍历(一次左到右,一次右到左),并给出了相应的C++代码实现。
摘要由CSDN通过智能技术生成

分发糖果(leetcode135)

题目:

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

分析:

vector<int>ratings//孩子评分数组

vector<int>num//分发糖果数量数组

本人思路:

首先找到评分最低的(如果同时有几个最低的那就找这几个),最低的分一个,然后向左右遍历,只能是大于或者等于的情况,大于就加一,等于不变,再过去可能就会小于,小于的话就给一个(达到最低的效果),然后因为同时有n个评分最低的,所以就会出现n种情况,要满足n种情况,就给每个孩子取情况里最大的 

但是感觉这样太复杂了

高畅笔记思路(两次遍历):

1.只需要简单的两次遍历即可:把所有孩子的糖果数初始化为 1
2.先从左往右遍历一遍,如果右边孩子的评分比左边的高,则右边孩子的糖果数更新为左边孩子的糖果数加 1
3.再从右往左遍历一遍,如果左边孩子的评分比右边的高,且左边孩子当前的糖果数不大于右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果数加 1
4.通过这两次遍历,分配的糖果就可以满足题目要求了。这里的贪心策略即为,在每次遍历中,只考虑并更新相邻一 侧的大小关系。

我只能说,太妙了,既然最低评分的可能有很多种情况,那我就先把所有的当成最低评分(全都初始化为1),然后从左到右遍历,右边大于左边就加一,若等于小于就就不变即还为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);
}
}

为什么要max(num[i-1], num[i] + 1)呢,因为你左边的数量可能是比右边要大一以上的

比如:

ratings:0 1 2 3 1 0

从左向右遍历后num:1 2 3 4 1 1

从右向左遍历后num:1 2 3 3 2 1

而满足两种情况的只能取更大值所以最后结果为1 2 3 4 2 1

完整代码:

int candy(vector<int>& ratings) {
int size = ratings.size();
if (size < 2) //如果孩子只有一个没有必要再算了
{
return size;
}
vector<int> num(size, 1);//初始化num数组里的元素全都为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); // std::accumulate 可以很方便地求和
}

 还有一种方法:常数空间遍历

先看几张图

 

发现规律没有,我们只要找到ratings数组里的递增和递减序列(如果像图三的第三个数一样既在递增序列里又在递减数列里那就算作数量更多的数列里,如果数量相等也算在递减的序列,图三即递减序列),然后从左到右第一个元素赋1,如为递增序列,右边比左边大则加一,等于则给一个(满足最小),递减序列的第一个值则等于递减序列的数量(若有等于的数字则只算一个,比如图三递减序列有4个数字,那就给递减序列的第一个赋4)

完整代码:

class Solution {
   public int candy(int[] ratings) {
       int n = ratings.length;
       int ret = 1;    //用于记录答案
       //pre用于记录前一个同学分得的糖果数量
       int inc = 1, dec = 0, pre = 1;
       for (int i = 1; i < n; i++) {
           if(ratings[i] >= ratings[i-1]){
               //处于递增序列中
               dec = 0;    //递减序列长度在递增序列中始终为0
               pre = ratings[i] == ratings[i- 1] ? 1 : pre+1;  //当前同学和上一个同学分数相等时,直接分配1个就行,这样满足最小
               ret += pre;
               inc = pre;      //inc用于记录上一个递增序列的长度

           }else {
               //处于递减序列中
               dec++;
               if(dec == inc){
                   //当递减序列长度和递增序列长度相等时,把递增序列的最后一个同学分配到递减序列中
                   dec++;
               }
               ret += dec; //这里加的dec相当于把递减序列翻转后加的每个同学的糖果数量
               pre = 1;    //pre在递减序列中没有意义,因为我肯定比前一个同学少;

           }
       }
       return ret;

   }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值