寿司 - 数组不相邻数之和最大值
此题来自于朋友在字节的笔试, 同类题目可见leetcode 198题 House Robber.
给你一堆寿司 不能选相邻的盘子 然后要选出价格加起来最高的那些 print价格
举例 [9 1 4 3] 输出13
如果是[9 1 3 2 5] 输出17
从题目知道这题本质是给一组数组,求数组里不相邻元素的最大值和,也就是说,求最优解。
思路
最优解题目使用DP。
第一步是 确定状态。
假设有10碟寿司,价格如下
寿司序列 | [0] | [1] | [2] | [3] | [4] | [5] | … | [8] | [9] |
---|---|---|---|---|---|---|---|---|---|
价格 | 9 | 1 | 4 | 3 | 5 | 6 | … | 8 | 10 |
不管是哪种组合,都存在算进组合里的最后一碟。
第二步,转移方程 。
O P T ( 9 ) OPT(9) OPT(9) --> 定义为当选择寿司[9]作为最后一步的最优解。
会出现两种情况:
如果选择寿司[9]作为最后一碟,那么因为题目元素不相邻的性质, 下一步计算不能选择寿司[8]。需要选择寿司[7],那么 O P T ( 9 ) = O P T ( 7 ) + a r r [ 9 ] OPT(9) = OPT(7)+arr[9] OPT(9)=OPT(7)+arr[9] . 简单来说就是当最后一步为寿司[7]的时候 ( O P T ( 7 ) ) OPT(7)) OPT(7)),加上寿司[9]的价格就是 O P T ( 9 ) OPT(9) OPT(9).
如果不选择寿司[9]作为最后一碟,那么我们只能选择[8]作为最后一碟,因为题目性质,最后一步必须为[8] 和[9]的其中之一。这种情况就是 O P T ( 8 ) OPT(8) OPT(8).
把整个算法步骤转为树状图:
总结就是,每一个最优解 O P T ( i ) OPT(i) OPT(i) 都有选和不选两种选择,如下:
也就是 $OPT(i) = $ max { O P T ( i − 2 ) + a r r [ i ] , O P T ( i − 1 ) OPT(i-2)+arr[i], OPT(i-1) OPT(i−2)+arr[i],OPT(i−1)}, 这个就是我们的**状态转移方程 **。
到这里已经是解开一半了,能通过递归求解得出,但是效率会很慢,因为有多个重复计算。
第三步,初始条件和边界条件。
很明显,
初始条件 OPT(0) = arr[0], OPT(1) = MAX{ a r r [ 0 ] , a r r [ 1 ] arr[0], arr[1] arr[0],arr[1]}
第四步,计算顺序。
这一步是为了优化效率,为了记录计算结果,不重复计算,从左到右开始算起。也即是 先从OPT(i=0)开始,然后往后计算。
代码
class Solution:
def sushi(self, nums: List[int]) -> int:
size = len(nums)
if (size == 0):
res = 0
elif size == 1:
res = int(nums[0])
# 边界条件
else:
# size >= 3
# 计算顺序从左到右
sum_a = nums[0] # 初始条件
sum_b = max(nums[0], nums[1])
for i in range(2, size):
temp = max(sum_a + nums[i], sum_b)
sum_a = sum_b
sum_b = temp
res = int(max(sum_a, sum_b))
return res