LeetCode 101 贪心算法(Candy (Hard))

贪心算法分配问题Candy (Hard)

题目描述

一群孩子站成一排,每一个孩子有自己的评分。现在需要给这些孩子发糖果,规则是如果一个孩子的评分比自己身旁的一个孩子要高,那么这个孩子就必须得到比身旁孩子更多的糖果;所有孩子至少要有一个糖果。求解最少需要多少个糖果。

输入输出样例

输入是一个数组,表示孩子的评分。输出是最少糖果的数量。

Input: [1,0,2]
Output: 5

在这个样例中,最少的糖果分法是 [2,1,2]。

题解

我们在求解这个问题时候首先要知道:
1.每个孩子都至少有一个糖果
2.在左右相邻的孩子中,贡献程度大的要比贡献程度小的孩子多一个糖果
3.最后把糖果加起来
这就浮现出来我们的解题思路,首先给所有的孩子发一颗糖果。
再就是解决左右相邻孩子发放糖果的问题

这里根据贡献大小的分布可分为三种情况
1.连续增长    例:贡献度[1,2,3,4,5] 糖果数[1,2,3]
2.波动变化    例:贡献度[1,5,4,3,2] 糖果数[1,4,3,2,1]
3.连续减小    例:贡献度[5,4,3,2,1] 糖果数[5,4,3,2,1]

根据贡献程度可以分为三种情况
1.左>右,2.左=右,3.左<右
第一种情况:左<右,右孩子比左孩子糖果多(通过上边情况二所以这里不能写(右=左+1))
第二种情况:左=右,不变
第三种情况:左>右,左孩子比右孩子糖果多(不能写(左=右+1))

在这里只需考虑“左>右”和“左<右”的情况,因为“左=右”时不发生任何变化。所以就用遍历的思想第一遍从左到右遍历“左>右”的情况,再从右到左遍历“左<右”的情况。

int candy(vector<int>& ratings) {
	int size = ratings.size();
	if (size < 2) {			 //注意一个孩子的情况一个孩子就一个糖果
		return size;
	}
	vector<int> num(size, 1);         //为每个孩子分发一个糖果
	for (int i = 1; i < size; ++i) {	
		if (ratings[i] > ratings[i-1]) {
			num[i] = num[i-1] + 1;    //第一遍遍历不用考虑波动变化的情况因为每个孩子才发了一个糖果
		}
	 }
	for (int i = size - 1; i > 0; --i) {
		if (ratings[i] < ratings[i-1]) {
			num[i-1] = max(num[i-1], num[i] + 1);//第二遍遍历需要考虑波动变化的情况
		}
	}
	return accumulate(num.begin(), num.end(), 0); // std::accumulate 可以很方便 地求和 }

补充:有上面两个分类的前提再深入思考一下,波动变化中的贡献度增减趋势变化处(糖果跟着贡献度的变化而变化),也可以说断点处又可分为几类(安从左到右遍历再从右到左遍历的先后顺序)
总:[a,d]   //下面假设拆分成两块
1.[a,b]:增加 ,[c,d]:增加   只在b,c处出现“左>右”的情况(注意这里b>c)
2.[a,b]:递减 ,[c,d]:递减   只在b,c处出现“左<右”的情况(注意这里b<c)
3.[a,b]:递增 ,[c,d]:递减   (注意这里b>c)
4.[a,b]:递减 ,[c,d]:递增   (注意这里b>c)
第一种情况
从左到右遍历:如果左<右,右=左+1。b的糖果>=1,c的糖果=1(若a,b只有一个元素b的糖果为1)。
从右到左遍历在b,c处:若b的糖果=1,则左=右+1,若b的糖果>1,则左糖果不变。从这里可以归纳出取num[i-1] = max(num[i-1], num[i] + 1)。
第二种情况
从左到右遍历:只在b,c处出现“左<右”的情况,右=左+1,从右到左遍历:出现“左>右”,左=右+1
第三种情况
从左到右遍历:只需注意b,c处,b,c处的糖果数不一定谁大,他们的大小取决于递增递减的次数
从右到左遍历到b,c时,以为b>c,所以取num[i-1] = max(num[i-1], num[i] + 1)。
第四种情况
从左到右遍历:如果左<右,右=左+1。
从右到左遍历:b,c处b>c。且c处的糖果数为1,则从b开始依次加1

需要注意的是这个算法在input:[1,2,2,2,2,1]时output:[1,2,1,1,2,1]要想输出[1,2,2,2,2,1]需再设定一个num[i-1]==num[i]的条件

这些都是作者自己一个字一个字码的,希望对你有所帮助,如果觉得对你有帮助,请给作者点个赞👍

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值