LeetCode 135.分发糖果
题目描述
老师想给孩子们分发糖果,有 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例1
输入: [1,0,2]
输出: 5
解释:
你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例2
输入: [1,2,2]
输出: 4
解释:
你可以分别给这三个孩子分发 1、2、1 颗糖果。 第三个孩子只得到 1 颗糖果,这已满足上述两个条件
题解
题目代码很简单,分别从左往右和从右往左扫描一次,取每个人的最大值相加。重点在于 为什么这么做是对的?
根据贪心的思路,应该确保尽可能每一个人少分糖果,如果符合条件就只给 个。而这里限制糖果个数的因素为:相邻的孩子中,评分高的孩子必须获得更多的糖果。
也即,每个孩子最终的糖果只依赖于其左右相邻的孩子,与更远的孩子无关。
这部分思路如果按照动态规划,可以理解为:如果孩子比上一个孩子评分高,那么他获得的糖果也应该更高。(逻辑上的上一个,与实际的顺序无关。按照题目要求,为相邻的两个孩子)
因此,实际上状态转移方程为
接下来要考虑的就是转移方程的独立性:为什么可以从两头计算?
上面的转移方程存在一个问题,就是在计算 时,需要预先拥有 和 ,但这两个数值的计算又依赖于 本身。因此常规的动态规划思路无法解决。
但是,如果再次观察这个方程,max
中的两个公式本身是不相关的,也即可以将其分成 和 两部分。
而这两部分是可以通过状态转移方程按顺序求解的,最后求每个位置的最大值即可。
原始的状态转移方程很容易写出,但是后续的化简可能会较难发现(但是如果真的把这个状态转移方程写出来,并且因为次序问题而觉得动态规划不能解决问题时,还是很可能发现化简方案的)
因此,对于这种问题,写出状态转移方程至关重要,即使状态转移方程可能无法直接求解
代码
class Solution: def candy(self, ratings: List[int]) -> int: n = len(ratings) left = [0] * n right = [0] * n res = 0 for i in range(n): j = n-i-1 left[i] = left[i-1] + 1 if i != 0 and ratings[i] > ratings[i-1] else 1 right[j] = right[j+1] + 1 if j != n-1 and ratings[j] > ratings[j+1] else 1 for i in range(n): res += max(left[i], right[i]) return res