LeetCode 笔记25 Candy (艰难的调试)
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?
本身题目我感觉不是太难。
楼主是这样想的。
使用一个candy数组[0...ratings.length - 1]
初始化第一个孩子得到一个糖,也就是candy[0] = 1;
从1开始扫描这个ratings数组,
如果遇到上升序列,candy[i] = candy[i - 1] + 1,同时拿一个lastHigh变量记录当前上升序列的index。换句话说,lastHigh存放的就是上一次波峰的峰值的index。
如果遇到相等的,candy[i] = 1。这里如果是face2face面试,你应该向面试官询问,如果ratings相等的孩子是否需要拿一样的糖。本题目没有这个要求,所以如果遇到相等的,我们立刻给一个糖,然后更新lastHigh。我第一次想的时候,坚信“lastHigh存放的就是上一次波峰的峰值的index”,所以我认为,“此步骤中,仅当ratings[lastHigh] == ratings[i], 才更新lastHigh为i”。这样,遇到相等的波峰,lastHigh始终指向最后一个高点(悬崖边上:>)。
如果遇到下降序列,这里有点复杂:
1)首先candy[i] = 1,因为要最小数目的糖嘛;
2)然后对从lastHigh + 1 到 i - 1 的candy,我们都要+1,那么,换句话说,total的糖的数目增加了i - lastHigh。注意到这是个优化,我们不需要真实地去遍历candy[lastHigh...i],依次去加1。
3)到了lastHigh,注意由于下降序列可能长度很长,造成candy[lastHigh + 1] == candy[lastHigh],那么,这时候我们也需要增加candy[lastHigh]。否则这一步不用做(candy[lastHigh] > candy[lastHigh + 1)
代码如下:
public int candy(int[] ratings) { if (ratings.length == 0) { return 0; } int[] candy = new int[ratings.length]; candy[0] = 1; int lastHigh = 0; int total = 1; for(int i = 1; i < ratings.length; i++) { if (ratings[i] > ratings[i - 1]) { candy[i] = candy[i - 1] + 1; lastHigh = i; total += candy[i]; } else if (ratings[i] == ratings[i - 1]) { candy[i] = 1 ; if (ratings[lastHigh] == ratings[i]) {//走到波峰的悬崖的边上 lastHigh = i; } total += candy[i]; } else { candy[i] = 1; total += i - lastHigh; if (i != lastHigh + 1) { candy[lastHigh + 1]++; } if (candy[lastHigh] == candy[lastHigh + 1]) { candy[lastHigh]++; total += 1; } } } //assertValid(ratings, candy); return total; }
然后,就悲催地发现,超大集合怎么都WA。
然后卡了很久(电影字幕:”十年后。。。“)
楼主把最后一个case的数组下载了下来,一看有9999个元素。只能写了一个文件去读。
因为自信算法上没有什么问题,肯定是什么corner case没考虑到,于是就在算法介绍后面写了个assert 函数去确认candy数组的合法性。主要就是考虑对每个元素,如果ratings大于左右两个邻居,那么candy数组也是如此。不过,我怀疑在判断相等ratings会不会有什么幺蛾子,就同时assert了:对于相等连续的rating元素,他们不能得到candy都一样(否则就不是最小candy数目了啊)。于是发现了这个序列(当前值是i,在3250):
i
ratings: 9734 8116 8116 3250
candy: 2 1 1 0
看出问题了么?lastHigh当时是在9734.
如果继续更新,按照算法,
candy: 3 2 2 1
可是,最小糖,对这个序列来说,应该是:
candy: 2 1 2 1
想了一下,原来lastHigh,不应该是记录波峰的,它的意义应该是:对于当前的i来说,记录能够随意增长而不受到前方其他限制的元素的index。
例如,在上面这个序列中,相等连续序列的最后一个(8116)应该是lastHigh,因为它能够无限制的增长,一满足后面更小的ratings抬高。
所以,上面代码唯一的改动,就是在遇到相等序列的时候,及时更新lastHigh变量。
public int candy(int[] ratings) { if (ratings.length == 0) { return 0; } int[] candy = new int[ratings.length]; candy[0] = 1; int lastHigh = 0; int total = 1; for(int i = 1; i < ratings.length; i++) { if (ratings[i] > ratings[i - 1]) { candy[i] = candy[i - 1] + 1; lastHigh = i; total += candy[i]; } else if (ratings[i] == ratings[i - 1]) { candy[i] = 1 ; lastHigh = i; total += candy[i]; } else { candy[i] = 1; total += i - lastHigh; if (i != lastHigh + 1) { candy[lastHigh + 1]++; } if (candy[lastHigh] == candy[lastHigh + 1]) { candy[lastHigh]++; total += 1; } } } //assertValid(ratings, candy); return total; }