leetcode135 分发糖果 题解

题目大意

一群小朋友站一排,然后每个小朋友有个rating值,现在让我们根据这个rating值来给每个小朋友分发糖果。但是分发的时候要按照以下规则:

  • 每个小朋友至少要分发一个糖果
  • 如果两个小朋友挨着,两个rating值一个大一个小,那么rating值大的那个小朋友必须比rating值小的那个小朋友分到更多的糖果

但是这里有一个恶心的地方,就是如果两个挨着的小朋友rating值一样,分到的糖果谁多谁少都无所谓(感觉题面出的很不切实际[doge])。

最后问你总共最少可以发多少颗糖果。
题目地址

思路

首先我们要排除一种特殊情况,如果大家的rating都是一样的,那么每个人都只能分到一颗糖果。

其次我们来贪心一下,在什么情况下,某个小朋友才会只被分到一颗糖果。

  1. 周围的小伙伴都不小于自己的时候,那你肯定只能得到一颗糖果
  2. 当你左边或者右边的小朋友跟你的rating值相同的时候(尤其是左右两边的都跟你一样的时候,肯定是1),有可能只被分到一颗糖果

那么什么时候小朋友可以被分到超过一颗糖果呢?

当发现一个严格递增或者严格递减的排列时,那么他们分到的糖果数也肯定是严格递增或者严格递减的。

然后就是代码实现的部分:

首先你先找出来周围的小朋友都大于自己的情况。

		f = 1;
        for (int i = N - 1; i >= 0; i--)
        {
            nums[i] = f;
            if (i != 0 && ratings[i] < ratings[i - 1])
                f = 0;
            if (i != 0 && ratings[i] > ratings[i - 1])
                f = 1;  
        }
        f = 1;
        for (int i = 0; i < N; i++)
        {
            nums[i] *= f;
            if (i != N - 1 && ratings[i] < ratings[i + 1])
                f = 0;
            if (i != N - 1 && ratings[i] > ratings[i + 1])
                f = 1;
        }

第一个 for 循环是判断哪些小朋友位于递增的子数列里,因为他右边的第一个和他不一样的值如果比他大的话,那么他所处的位置肯定就是递增的,但是一定要注意最后一个位置,即使他左边的值比他大,他也有可能只被分到一颗糖果,一会给大家举个例子。

同理,第二个 for 循环是判断哪些小朋友处于递减的子数列里面,因为他左边的第一个跟他不一样的值如果比他大的话,就是处于递减的位置。同样也要注意第一个位置。

那么现在,同时处于递减和递增的位置(第一个和最后一个位置除外)的小朋友是不是肯定就只被分到了一颗糖果。

我们用下面的例子来举例。

r a t i n g s : [ 1 , 2 , 3 , 4 , 4 , 3 , 2 ] ratings: [1, 2, 3, 4, 4, 3, 2] ratings:[1,2,3,4,4,3,2]

如果我们按照上面的代码进行运行的话,那么第一次 for 循环执行过后,nums(用来存储每个人分到了多少颗糖果)的结果如下:

n u m s : [ 1 , 1 , 1 , 0 , 0 , 0 , 1 ] nums: [1, 1, 1, 0, 0, 0, 1] nums:[1,1,1,0,0,0,1],虽然 3 的位置也处于一个递增的位置上,但是右边还有数字比他大,所以无伤大雅。

第二次 for 循环,我们处理完之后,nums 变成了:
n u m s : [ 1 , 0 , 0 , 0 , 0 , 0 , 1 ] nums: [1, 0, 0, 0, 0, 0, 1] nums:[1,0,0,0,0,0,1]

最后我们以所有分到一颗糖的小朋友为中心,向周围扩展,看看周围的小朋友最少分到多少颗糖果(在不违反规则的情况下)。

代码

class Solution {
public:
    int candy(vector<int>& ratings) {
        int N = ratings.size();
        vector<int> nums(N);
        int f = false;
        for (int i = 1; i < N; i++)
        {
            if (ratings[i] != ratings[i - 1])
            {
                f = true;
                break;
            }
        }
        if (!f)
            return N;

        f = 1;
        for (int i = N - 1; i >= 0; i--)
        {
            nums[i] = f;
            if (i != 0 && ratings[i] < ratings[i - 1])
                f = 0;
            if (i != 0 && ratings[i] > ratings[i - 1])
                f = 1;  
        }
        for (int i = 0; i < N; i++)
            cout << nums[i] << ' ';
        cout << '\n';
        f = 1;
        for (int i = 0; i < N; i++)
        {
            nums[i] *= f;
            if (i != N - 1 && ratings[i] < ratings[i + 1])
                f = 0;
            if (i != N - 1 && ratings[i] > ratings[i + 1])
                f = 1;
        }
        for (int i = 0; i < N; i++)
            cout << nums[i] << ' ';
        cout << '\n';

        for (int i = 0; i < N; i++)
        {
            if (nums[i] == 1)
            {
                nums[i] = 1;
                int j = i - 1;
                while (j >= 0 && ratings[j] >= ratings[j + 1])
                {
                    if (ratings[j] == ratings[j + 1])
                        nums[j] = max(nums[j], 1);
                    else
                        nums[j] = max(nums[j], nums[j + 1] + 1);
                    j--;
                }
                j = i + 1;
                while (j < N && ratings[j] >= ratings[j - 1])
                {
                    if (ratings[j] == ratings[j - 1])
                        nums[j] = max(nums[j], 1);
                    else
                        nums[j] = max(nums[j], nums[j - 1] + 1);
                    j++;
                }
            }    
        }

        int ans = 0;
        for (int i = 0; i < N; i++)
            ans += nums[i];
        return ans;
    }
};

结果

在这里插入图片描述






作者能力有限,如果有任何错误之处,还请各位指教。(~ ̄▽ ̄)~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值