【算法刷题】leetcode Candy

博客详细介绍了LeetCode上Candy问题的两种解法,包括扫两遍数组的策略和一次遍历修正的技巧,重点讨论了如何在保证每个孩子至少一个糖果的前提下,根据评级公平分配糖果的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?


解法1:

扫两遍数组:(初始个数都为1个)第一次,从左往右扫,右边的rating大于左边的,则右边的个数=左边个数+1

                                                     第二次,从右往左扫,左边的rating大于右边的,

                                                           则左边的个数=max(左边个数,右边个数+1)


    int candy(vector<int> &ratings) {
        int n = ratings.size();
        if(n==0)
            return 0;
        int res(0);
        vector<int> nnum(n,1);
        for(int i=1;i<n;++i)
        {
            if(ratings[i]>ratings[i-1])
                nnum[i]=nnum[i-1]+1;
        }
        for(int i=n-1;i>0;--i)
        {
            if(ratings[i-1]>ratings[i])
                nnum[i-1]=max(nnum[i-1],nnum[i]+1);
        }
        
        for(auto a:nnum)
            res+=a;
        
        return res;
    }

解法2:只遍历一次ratings,需要计算递减序列的长度:该部分解法转自

请回想一下:我们为什么需要辅助空间?当孩子的rate是一个非递减曲线的时候,我们是不需要辅助空间的,比如5个孩子的rate分别是1,2,5,7,10。那么糖果数自然是1,2,3,4,5。又如5个孩子的rate分别是1,2,5,5,10,那么糖果数自然是1,2,3,1,2。

因此如果rate是非递减数列,我们可以精确计算出当前孩子应该给多少糖果,把这个糖果数加入总数即可。

当孩子的rate出现递减的情况该如何是好?不用辅助空间能处理吗?

假设5个孩子的rate是 1,5,4,3,2。我们这样计算:遍历时,第一个孩子依然糖果为1,第二个孩子糖果为2,第三个孩子糖果给几个?我们遍历到后面就会知道第二个孩子给的糖果太少了,应该给4个。有没有办法在遍历到后面时,能计算出一个修正值,使得加上这个修正值,正好依然可以使总糖果数是正确的?

 

其实这个修正值不难计算,因为可以发现递减数列的长度决定了第二个孩子该给几个糖果。仔细观察:遍历到第四个孩子时我们知道了第二个孩子不该给2,应该给3,因此Total 要 +=1;遍历到第五个孩子我们知道了第二个孩子不该给3得给4,因此Total 要 += 1。我们设一个变量beforeDenc表示进入递减序列之前的那个孩子给的糖果值,再设置length用来表达当前递减序列的长度。这两个变量就可以决定Total是不是要修正:当遍历第三个孩子的时候 beforeDenc = 2,以后每遍历一个孩子,因为length已经超过了beforeDenc,每次Total都要额外+1,来修正第二个孩子的糖果数。

 

对于后面三个孩子,我们可以这样计算:遍历到第三个孩子,因为这是递减数列的第二个数字,我们Total += 1;第四个孩子是递减数列的第三个数字,Total += 2;第五个孩子是递减数列的第四个数字,Total += 3。

可以发现最后三个孩子的糖果总数依然是正确的,虽然Total 每次增加的糖果数量正好和当前孩子得到的糖果数是反序关系。

 

这种边遍历边修正的方法可以保证一次遍历,不需要O(n)空间下计算出Total的正确值。



    int candy(vector<int> &ratings) {
       
    int Total = 0;    /// Total candies
    int length = 0;  /// Continuous descending length of rate
    int nPreCanCnt = 1; /// Previous child's candy count
    int beforeDenc = nPreCanCnt;
    if(ratings.begin() != ratings.end())
    {
        Total++; //Counting the first child's candy (1).
        for(vector<int>::iterator i = ratings.begin()+1; i!= ratings.end(); i++)
        {
            if(*i < *(i-1))
            {
                length++;
                //将递减序列的第一个数字补齐。
                if(beforeDenc <= length)
                {
                    Total++;
                }
                Total += length;
                nPreCanCnt = 1;    //This step is important, it ensures that once we leave the decending sequence, candy number start from 1
            }
            else
            {
                int curCanCnt = 0;
                if(*i > *(i-1))
                { 
                    curCanCnt = (nPreCanCnt + 1);
                }
                else
                {
                    curCanCnt = 1;
                }
                Total += curCanCnt;
                nPreCanCnt = curCanCnt;
                length = 0;    //reset length of decending sequence
                beforeDenc = curCanCnt;
            }
        }
    }
    return Total;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值