【Leetcode】135、分发糖果 Hard

题目概述:

分发糖果:

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

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

每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

 

 

思路:

考虑需要调整的情况:

(1)左边的孩子ratings比右边高但是糖果不多于右边的孩子

(2)左边的孩子ratings比右边低但是糖果不少于右边的孩子

遇到需要调整的情形,如何调整:采用贪心算法

(1)if  left_child_ratings > right_child_ratings  && left_child_candies <= right_child_candies:   left_child_candies = right_child_candies + 1

(2)if  left_child_ratings < right_child_ratings  && left_child_candies >= right_child_candies:   right_child_candies = left_child_candies + 1

具体策略:

设置一个长度为2的窗口,先从左向右滑动,检查并调整,再从右向左滑动, 检查并调整。

 

算法合理性的分析:

窗口从左向右滑动的过程,遇到 left_child_ratings < right_child_ratings  && left_child_candies >= right_child_candies 的情况, 调整,

窗口滑过去后,只可能增加这个right_child_candies, 此时仍满足 left_child_ratings < right_child_ratings  && left_child_candies < right_child_candies ;

但是如果遇到left_child_ratings > right_child_ratings  && left_child_candies <= right_child_candies 的情况, 调整完,

窗口滑动过去后,可能又会增加 right_child_candies, 破坏之前满足的 left_child_ratings > right_child_ratings  && left_child_candies > right_child_candies, 

此时我们需要, 当窗口从左向右滑到底后, 再将窗口从右向左滑到, 采取之前的调整方法

 

我最初的代码:

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int>candies(ratings.size(), 1);
        for (int i = 0; i < ratings.size() - 1; ++i)
        {
            if (ratings[i] > ratings[i+1] && candies[i] <= candies[i+1])    
                candies[i] = candies[i+1] + 1;
            if (ratings[i] < ratings[i+1] && candies[i] >= candies[i+1])
                candies[i+1] = candies[i] + 1;
        }
        for (int i = ratings.size() - 1; i > 0; --i)
        {
            if (ratings[i] > ratings[i-1] && candies[i] <= candies[i-1])
                candies[i] = candies[i-1] + 1;
            if (ratings[i] < ratings[i-1] && candies[i] >= candies[i-1])
                candies[i-1] = candies[i] + 1;
        }
        return accumulate(candies.begin(), candies.end(), 0);
    }
};

其实, 经过上述分析, 代码可以简化

(1)在窗口从左向右滑动的过程, 只需考虑 left_child_ratings < right_child_ratings  && left_child_candies >= right_child_candies 的情况

滑过一轮后    left_child_ratings < right_child_ratings  -->   left_child_candies < right_child_candies

(2)窗口再从右向左滑动, 只需考虑 left_child_ratings > right_child_ratings  && left_child_candies <= right_child_candies 的情况

滑过一轮后    left_child_ratings > right_child_ratings  -->   left_child_candies > right_child_candies

且第二轮调整时不会破坏第一轮的调整结果, 因为每次调整, left_child_candies增加, 窗口滑动一次作为right_left_candies

如果left_child_ratings < right_child_ratings仍保持 left_child_candies < right_child_candies (因为right_child_candies相对第一轮只增不减)

简化的代码:

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int>candies(ratings.size(), 1);
        for (int i = 0; i < ratings.size() - 1; ++i)
        {
            if (ratings[i] < ratings[i+1] && candies[i] >= candies[i+1])
                candies[i+1] = candies[i] + 1;
        }
        for (int i = ratings.size() - 1; i > 0; --i)
        {
            if (ratings[i] < ratings[i-1] && candies[i] >= candies[i-1])
                candies[i - 1] = candies[i] + 1;
        }
        return accumulate(candies.begin(), candies.end(), 0);
    }
};

 

本题思想简单, 但实际上细节理解比较复杂。

核心是第二次遍历如何才能在调整自己的基础上维护第一次遍历得到的结构

 

posted @ 2022-02-22 10:35  鱼儿冒个泡  阅读(47)  评论(0)    收藏  举报