89.分发糖果

一、题目描述

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

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

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

二、解题思路

2.1 思路一:暴力求解

先根据题意简单模拟一下解题过程,看下图,输入是每个孩子的评分,每个孩子初始都能分到一个糖果。
在这里插入图片描述
第一步:对于第一个位置,孩子的评分是1,比较他的左右评分,左边没有不用比较,评分比右边孩子的小,所以不用改变糖果数量,继续依次从左往右遍历:
在这里插入图片描述

通过分析发现,第二个位置小孩的评分比右边的小孩子低,所以先不变,但是它比左边孩子的评分高,所以糖果数要加一个,变成2。

第二步:继续遍历第三个小孩子的评分87,它和右边的孩子评分一样,所以糖果数先不变,但是它的评分又比左边孩子的高,所以糖果数也要比左边孩子的高:
在这里插入图片描述

第三步:继续遍历第四个孩子,它的评分和左右孩子的一样,所以糖果数不变,那么直接看第五个孩子的评分,比它右边的高,所以糖果数加一,和左边孩子一样,糖果数保持不变。
在这里插入图片描述

第四步:遍历到第六个孩子的评分2,它比它右边的孩子评分高,所以糖果数加一,比左边的小,加一后先保持不变:
在这里插入图片描述

第五步:遍历最后一个孩子的评分,它的评分比左右的都小,所以糖果数不变。至此,所有糖果分发完毕。

这里应该会注意到一个问题,就是第五个孩子的评分比第六个孩子的评分高,但是糖果数和第六个孩子是一样的,那怎么解决呢?答案就是再从左到右再遍历一遍即可。这里就不逐一展示了,当遍历到第五个孩子时,糖果数加一即可:
在这里插入图片描述

2.2 思路二:两个数组+两次遍历

使用两个数组,一个数组(left2Right)记录从左往右遍历评分时糖果数的变化,一个数组(right2Lift)记录从右往左遍历评分的变化。

在这里插入图片描述

当从左往右遍历的时候,利用left2Right数组记录孩子糖果数的变化,遍历每一个孩子的评分时,每个孩子只与左边的孩子的评分进行比较,如果当前孩子的评分比左边的孩子大,那么糖果数就加一。

当从右往左遍历的时候,利用right2Left数组记录孩子糖果数的变化,遍历每一个孩子的评分时,每个孩子只与右边的孩子的评分进行比较,如果当前孩子的评分比右边的孩子大,那么糖果数就加一。

在这里插入图片描述

最终的糖果数统计每个孩子在两个数组left2Rightright2Left中的最大值就行,第一个孩子就是1,第二个孩子是2,第三个孩子是3,以此类推。最终得到:

在这里插入图片描述

三、代码演示

3.1 暴力求解

//定义小孩子的个数
        int n = ratings.length;

        //定义每个小孩子得到糖果的数组
        int[] candies = new int[n];
        //先给每个小孩子赋值一个糖果
        Arrays.fill(candies,1);

        //使用一个变量表示糖果有变化
        boolean hasChange = true;
        while (hasChange){
            //一开始糖果数没有变化,就是false
            hasChange = false;
            //遍历每一个孩子的评分
            for (int i=0; i<n; i++){
                /**
                 * 与右边孩子比较
                 * 三个条件:1.不是最右边的孩子。2.评分大于右边的孩子。3.糖果数小于右边的孩子
                 */
                if (i!=n-1 && ratings[i]>ratings[i+1] && candies[i]<=candies[i+1]){
                    candies[i] = candies[i+1] + 1;
                    hasChange = true; //糖果数有变化就设置为true
                }
                /**
                 * 与左边孩子比较
                 * 三个条件:1.当前孩子不是最左边的孩子。2.评分大于左边孩子。3.糖果数小于等于左边孩子
                 */
                if (i!=0 && ratings[i]>ratings[i-1] && candies[i]<=candies[i-1]){
                    candies[i] = candies[i-1] + 1;
                    hasChange = true;
                }
            }
        }

        int sum = 0;
        //累加糖果数
        for (int candy : candies){
            sum += candy;
        }
        return sum;

时间复杂度是O(n2):假如说对于一个降序排列的评分,从左往右遍历每循环一次只会算对一个孩子的糖果数(可以自己试试),所以要循环n次,时间服复杂度就是O(n2)。

3.2 两个数组+两次遍历

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;

        //两个数组分别记录从左往右和从右往左遍历时的糖果数变化,初始都是1个糖果
        int[] left2Right = new int[n];
        Arrays.fill(left2Right,1);
        int[] right2Left = new int[n];
        Arrays.fill(right2Left,1);

        //从左往右
        for (int i=0; i<n; i++){
            if (i!=0 && ratings[i] > ratings[i-1]){
                left2Right[i] = left2Right[i-1] + 1;
            }
        }
        //从右往左
        int sum = 0;
        for (int i=n-1; i>=0; i--){
            if (i!=n-1 && ratings[i]>ratings[i+1]){
                right2Left[i] = right2Left[i+1]  + 1;
            }
            //将两个数组中的最大值累加就是最大糖果数
            sum += Math.max(left2Right[i], right2Left[i]);
        }
    return sum;
    }
}

时间复杂度是O(n),因为只经历了两次遍历

空间复杂度O(n),因为申请了两个数组(精确一点应该是2n)

3.3 思路二代码优化

只用一个数组,优化空间,使用一个变量来记录从右往左遍历时的值

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;

        //使用一个数组记录从左往右的糖果数变化,初始都是1个糖果
        int[] left2Right = new int[n];
        Arrays.fill(left2Right,1);

        //从左往右
        for (int i=0; i<n; i++){
            if (i!=0 && ratings[i] > ratings[i-1]){
                left2Right[i] = left2Right[i-1] + 1;
            }
        }
        //从右往左
        int sum = 0;
        //使用一个变量来存储从右往左遍历时的糖果数量变化
        int right = 0;
        for (int i=n-1; i>=0; i--){
            if (i!=n-1 && ratings[i]>ratings[i+1]){
                right++;
            }else {
                right = 1; //不符合if中的条件,糖果数重新置为1
            }
            //将两个数组中的最大值累加就是最大糖果数
            sum += Math.max(left2Right[i], right);
        }
        return sum;
    }
}
<think>好的,我现在需要解决用户的问题,即介绍C++中的分发糖果问题。首先,我要理解这个问题的大致内容。分发糖果通常是指LeetCode上的一个题目,题目描述是孩子们排一排,每个孩子有一个评分,要求按照一定规则分发糖果,使得每个孩子至少有一个糖果,且评分更高的孩子必须比相邻的孩子获得更多的糖果。最终要计算最少需要多少糖果。 接下来,我需要回忆这个题目的具体要求和解决方法。通常,这个问题可以通过两次遍历来解决:一次从左到右,确保右边评分更高的孩子比左边多;另一次从右到左,处理左边评分更高的孩子。然后将两次遍历的结果取最大值,总和即为最少糖果数。 我需要用C++来实现这个算法,所以要考虑如何编写代码。首先,定义一个数组来存储每个孩子的糖果数,初始化为1。然后进行两次遍历:第一次从左到右,如果右边评分高,则右边孩子的糖果数为左边加一;第二次从右到左,如果左边评分高,则左边孩子的糖果数取当前值和右边加一的最大值。最后累加所有糖果数。 在解释过程中,要确保符合用户给出的系统级指令,比如数学表达式用$...$,独立公式用$$...$$,并且使用中文。需要注意代码部分是否需要用LaTeX排版,但可能直接使用代码块更合适,不过用户没有特别说明,可能需要询问,但根据问题描述,用户可能更希望看到代码示例。 现在需要检查是否有遗漏的情况,比如所有孩子评分相同的情况,或者评分递增或递减的情况。比如评分是[1,2,3],那么分发应该是1,2,3,总和6;如果评分是[3,2,1],则分发3,2,1,总和6。如果评分是[1,3,4,5,2],则需要正确处理好峰值的情况。 在编写代码时,要注意数组的初始化,两次遍历的顺序,以及最后的累加。可能需要测试几个例子来验证代码的正确性。 现在组织回答的结构:先介绍问题,再分步骤解释解决方法,然后给出代码示例,最后分析时间复杂度和可能的优化点。确保结构清晰,步骤明确,符合用户的要求。 需要确认是否所有数学表达式都正确使用了$符号,例如在说明条件时,比如右边的评分比左边高,即ratings[i] > ratings[i-1],可能需要写$ratings[i] > ratings[i-1]$,但实际代码中可能不需要数学表达式,所以可能不需要。但如果有提到变量,比如评分数组,可以用$ratings$表示。 最后,确保回答使用中文,语法正确,LaTeX格式正确,并且独立公式单独段。不过在这个问题中可能涉及的数学公式较少,主要是算法步骤和代码,但如果有需要,比如计算糖果数的公式,可以用独立公式。</think>### C++实现分发糖果问题解析 #### 问题描述 给定一个长度为$n$的整数数组$ratings$,表示$n$个孩子的评分。需按以下规则分发糖果1. 每个孩子至少1糖果 2. 评分更高的孩子必须比相邻孩子获得更多糖果 求最少需要多少糖果。 #### 解决思路 采用**两次贪心遍历**策略: 1. **从左向右遍历**:确保右边孩子比左边评分高时,糖果数更多 2. **从右向左遍历**:确保左边孩子比右边评分高时,糖果数更多 #### 算法步骤 1. 初始化数组$candies$,每个元素为1 2. 左→右遍历:若$ratings[i] > ratings[i-1]$,则$candies[i] = candies[i-1] + 1$ 3. 右→左遍历:若$ratings[i] > ratings[i+1]$,则$candies[i] = \max(candies[i], candies[i+1] + 1)$ 4. 累加所有糖果数 #### 时间复杂度 $$O(n)$$ 仅需两次线性遍历,时间复杂度为线性级别 --- ### C++代码实现 ```cpp #include <vector> #include <algorithm> using namespace std; int candy(vector<int>& ratings) { int n = ratings.size(); if(n == 0) return 0; vector<int> candies(n, 1); // 从左向右遍历 for(int i = 1; i < n; ++i) { if(ratings[i] > ratings[i-1]) { candies[i] = candies[i-1] + 1; } } // 从右向左遍历 for(int i = n-2; i >= 0; --i) { if(ratings[i] > ratings[i+1]) { candies[i] = max(candies[i], candies[i+1] + 1); } } // 累加结果 int total = 0; for(int num : candies) { total += num; } return total; } ``` --- ### 实例验证 例1:输入$ratings = [1,0,2]$ - 第一次遍历后:$[1,1,2]$ - 第二次遍历后:$[2,1,2]$ - 总数:$2+1+2=5$ 例2:输入$ratings = [1,3,4,5,2]$ - 第一次遍历后:$[1,2,3,4,1]$ - 第二次遍历后:$[1,2,3,4,1]$(右遍历无变化) - 总数:$1+2+3+4+1=11$ --- ### 关键点说明 1. **双向遍历必要性**:仅单方向遍历无法处理递减序列 2. **取最大值操作**:保证同时满足左右两边约束条件 3. **空间复杂度优化**:可将数组改为单个变量,但会降低代码可读性 此解法严格满足题目要求,且是最优时间复杂度解决方案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值