根据游戏得分数组arr,给人分糖果,至少要准备多少糖果才行?

根据游戏得分给孩子分糖果,至少要准备多少糖果才行?

提示:当年,我参加华为的笔试,遇到一个分糖果的题目,当时我由于第一题太难,一直没空思考这个分糖果的题目!
但是我清楚滴知道,我曾今见过一个分糖果的题目,当时我就发誓,要把这个分糖果的题目类型搞清楚
今天,我就要搞清楚这个题目原型,然后试图破解一下华为的笔试分糖果问题!

本题好像跟华为的笔试题有点区别,解法不太一样
给一堆糖果,要求给2个人分配数量一样的糖果,请返分的的糖果数量


题目

一群孩子做游戏,每个人有一个得分,放在arr中,现在请你根据游戏得分arr来发糖果,要求如下:
1.每个孩子,不管得分多少,至少 发一个糖果。
2.任意两个相邻的孩子之前,如果得分较多,那个孩子必须比另一个多拿一些糖果。
请你计算至少要准备多少糖果?才能给满足这些要求。

进阶2:如果相邻的得分一样,至少他们要拿一样数量的糖果,请问你至少要准备多少糖果?
进阶3:如果要求上面两个题目,时间复杂度都为o(n),空间复杂度都为o(1),算法怎么优化?


一、审题

示例:
(1)arr=122,至少要这么分,121,因此准备最少4块糖果
第二个比第一个多,不妨设第一个1块,那第二个至少比第一个多1块(题目说了多一些,那就暂定最少1块)
而第二个,第三个人得分一样,没有要求他们非要一样多,所以至少1块就行。【当然这么分其实不合理的】
(2)进阶2,既然相邻2个得分一样,那至少要发给第二个第三个人2块糖果,最后这么发:122,所以最少准备5个糖果。


二、解题

原始题目解法

这个题,我们观察一下,要从得分最少那个人开始算,至少1颗糖果,如果相邻的人,比我得分多,在我基础上增加糖果即可
相邻的得分比我少,或者得分一样的,他们至少一块糖
看绿色这,如果咱们从左往右发糖果,就会发现3分那个人只能拿2个糖
但是,当咱们从右往左发糖果,就会发现,其实3分那个人是可以拿3个糖果的
在这里插入图片描述
说明什么问题?
咱不能单独从左或者单独从右发糖果
而是要从2侧发一遍,然后根据最大糖果数来发

这么理解,咱们要找到得分的低谷,然后从低谷开始,上坡的递增糖果,谷峰那个位置,取决于谁?
取决于它左右两边最低点那个糖果数
看图:对于数组arr=3 2 1 4 6 2 3 5 2 1 0
找到谷底i=2位置
咱往左发糖果,那就是123块,往有发糖果就是123
再看谷底i=5位置,往左发糖果就是12,往右发糖果就是123
再看谷底i=10,往左边发糖果就是1234
显然:
在谷峰i=0处,3块糖
在谷峰i=4处,不是2,应该是3块糖
在谷峰i=7处,不是3,而是4块糖
在这里插入图片描述
好,理解了从谷底开始发1块,往谷峰递增的原理,就知道
本题的解题大流程就是:
——预设2个数组
left为从左往右发糖果的数量:
(1)i=0的,发1个糖果
(2)当i>=1,判断arr[i]>arr[i-1]则糖果数量递增,否则就发1颗糖就行
right为从右往左发糖果的数量:
(1)i=N-1的,发1个糖果
(2)当i<N-1,判断arr[i]>arr[i+1]则糖果数量递增,否则就发1颗糖就行
——有了left,结果ans自然就是遍历left和right,选最大值相加即可

很简单的:

//1)请问你至少需要多少个糖:
    //比如:1,2,2,至少分1,2,1块==4块,只看相邻分差大的,不管一样分数的
    //这个题,需要辅助数组,得分最小的谷底,得1块糖,然后往两边看,上坡+1块
    //遇到山峰,那到底选左边上坡来的呢,还是右边呢,当然是最大值那个坡,否则就没法让最下边那个相邻差糖的数量区分开
    public static int leastCandy(int[] arr){
        if (arr == null || arr.length == 0) return 0;

        int N = arr.length;
        int[] left = new int[N];
        int[] right = new int[N];//从左开始发糖,至少每个人1块糖
        left[0] = 1;
        right[N - 1] = 1;
        //从左边上坡分糖果
        for (int i = 1; i < N; i++) {
            if (arr[i] > arr[i - 1]) left[i] = left[i - 1] + 1;
            else left[i] = 1;//<=时,只需要1块糖就行了
        }
        //从右边上坡分糖果
        for (int i = N - 2; i >= 0; i--) {
            if (arr[i] > arr[i + 1]) right[i] = right[i + 1] + 1;
            else right[i] = 1;//<=时,只需要1块糖就行了
        }
        int max = 0;
        //看哪个坡更高,就要哪个糖果数
        for (int i = 0; i < N; i++) {
            max += Math.max(left[i], right[i]);//只需要坡度更大的那块糖数量
        }

        return max;
    }

进阶2题目解法

有了上面的方法,就太容易不过了
上面那个相邻的人,分数一样,也只发1个糖果,现在,要求,相邻数量一样,则要发一样的糖果
本题的解题大流程就是:
——预设2个数组
left为从左往右发糖果的数量:
(1)i=0的,发1个糖果
(2)当i>=1,判断arr[i]>arr[i-1]则糖果数量递增(区别在这,就是严格大于才行)
(3)当i>=1,判断arr[i]=arr[i-1]则发的糖果数量与i-1位置一样
right为从右往左发糖果的数量:
(1)i=N-1的,发1个糖果
(2)当i<N-1,判断arr[i]>arr[i+1]则糖果数量递增(区别在这,就是严格大于才行)
(3)当i<N-1,判断arr[i]=arr[i+1]则发的糖果数量与i+1位置一样
——有了left,结果ans自然就是遍历left和right,选最大值相加即可
代码也很简单

//2)如果相邻两个得分一样,必须分相同数量的糖果,至少需要多少块?
    //比如1,2,2,至少分1,2,2==5块
    //和上面一样,左右两种分法,不过,当相邻分数一样,请维持糖果的数量,相等
    public static int leastCandySameScore(int[] arr){
        if (arr == null || arr.length == 0) return 0;

        int N = arr.length;
        int[] left = new int[N];
        int[] right = new int[N];//从左开始发糖,至少每个人1块糖
        left[0] = 1;
        right[N - 1] = 1;
        //从左边上坡分糖果
        for (int i = 1; i < N; i++) {
            if (arr[i] > arr[i - 1]) left[i] = left[i - 1] + 1;
            else if (arr[i] == arr[i - 1]) left[i] = left[i - 1];//维持,和题1的区别
            else left[i] = 1;//<=时,只需要1块糖就行了
        }
        //从右边上坡分糖果
        for (int i = N - 2; i >= 0; i--) {
            if (arr[i] > arr[i + 1]) right[i] = right[i + 1] + 1;
            else if (arr[i] == arr[i + 1]) right[i] = right[i + 1];//维持
            else right[i] = 1;//<=时,只需要1块糖就行了
        }
        int max = 0;
        //看哪个坡更高,就要哪个糖果数
        for (int i = 0; i < N; i++) {
            max += Math.max(left[i], right[i]);//只需要坡度更大的那块糖数量
        }

        return max;
    }

    //要求空间o(1)的话,就复杂了,但是就是找拐点就行,有机会看左神的代码,很复杂

进阶3:如果要求上面两个题目,时间复杂度都为o(n),空间复杂度都为o(1),算法怎么优化?

arr复用
先找拐点i
(1)i=0,或者i=N-1就是拐点
(2)其他的i,保证arr[i-1]>=arr[i],arr[i+1]<=arr[i]
我的想法是谷峰位置j与左右2侧的拐点g1和g2,看哪个距离越远,说名数量越多,这样的话用数量多那边拐点开始的糖果数递增,一定保证结果最大,求和即可,笔试用上面两个方法,面试先讲上面的两个方法,然后聊一下这个笔试的思想即可。
在这里插入图片描述
代码就不写了。


总结

提示:重要经验:

1)2个发糖果的策略,基本思想就是看坡谁更大,选谁
2)思考一下这个题,对于华为那个发糖果的题目有没有帮助,然后彻底破解这个类型的题目!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值