Leetcode 1388:3n块披萨(超详细的解法!!!)

给你一个披萨,它由 3n 块不同大小的部分组成,现在你和你的朋友们需要按照如下规则来分披萨:

  • 你挑选 任意 一块披萨。
  • Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。
  • Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。
  • 重复上述过程直到没有披萨剩下。

每一块披萨的大小按顺时针方向由循环数组 slices 表示。

请你返回你可以获得的披萨大小总和的最大值。

示例 1:

输入:slices = [1,2,3,4,5,6]
输出:10
解释:选择大小为 4 的披萨,Alice 和 Bob 分别挑选大小为 3 和 5 的披萨。然后你选择大小为 6 的披萨,Alice 和 Bob 分别挑选大小为 2 和 1 的披萨。你获得的披萨总大小为 4 + 6 = 10 。

示例 2:

输入:slices = [8,9,8,6,1,1]
输出:16
解释:两轮都选大小为 8 的披萨。如果你选择大小为 9 的披萨,你的朋友们就会选择大小为 8 的披萨,这种情况下你的总和不是最大的。

示例 3:

输入:slices = [4,1,2,5,8,3,1,9,7]
输出:21

示例 4:

输入:slices = [3,1,2]
输出:3

提示:

  • 1 <= slices.length <= 500
  • slices.length % 3 == 0
  • 1 <= slices[i] <= 1000

解题思路

这个问题和之前问题Leetcode 213:打家劫舍 II类似,区别在于这个问题中我们只可以选n//3个数,而之前的问题可以选(n-1)//2个数,但是思路相同。

对着这种环形问题只需分成两种情况讨论:1)第一个位置选。 2)最后一个位置选。(本质就是保证首尾不相邻)

该过程就是将环形问题转为非环问题,非环问题就是Leetcode 198:打家劫舍n//3个数限制的情况。

from functools import lru_cache
class Solution:
    def maxSizeSlices(self, nums: List[int]) -> int:
        n = len(nums)
        
        @lru_cache(None)
        def dfs(cur, r, k):
            if k == 0: return 0
            if cur >= r: return 0
            return max(dfs(cur + 1, r, k), dfs(cur + 2, r, k - 1) + nums[cur])
        return max(dfs(0, n - 1, n // 3), dfs(1, n, n // 3))

按照和之前问题相同的处理手法,可以写出相应的动态规划形式。

class Solution:
    def maxSizeSlices(self, nums: List[int]) -> int:
        n = len(nums)
        k = n // 3
        
        def cal(l, r):
            dp = [[0] * (k + 1) for _ in range(n + 2)]
            for i in range(l, r):
                for j in range(k):
                    dp[i][j] = max(dp[i - 1][j], dp[i - 2][j - 1] + nums[i])
            return dp[r - 1][k - 1]
        return max(cal(0, n - 1), cal(1, n))

采用和之前问题相同的思路优化空间复杂度。

class Solution:
    def maxSizeSlices(self, nums: List[int]) -> int:
        n = len(nums)
        k = n // 3
        
        def cal(l, r):
            dp = [[0] * (k + 1) for _ in range(2)] 
            for i in range(l, r):
                
                dp[0], dp[1] = dp[1], [j and max(nums[i] + dp[0][j - 1], dp[1][j]) for j in range(k + 1)]
            return dp[-1][-1]
        return max(cal(0, n - 1), cal(1, n))

上面两种写法比较特别,第一种写法充分利用python的切片特性,可以通过-1访问最后一个元素,这一样我们就不同考虑j=0的情况。第二种写法为了维护空间大小的一致性,通过j and ...表达式判断j=0的情况。如果也想像第一种那样去写的话,可以这样:

dp[0], dp[1] = dp[1], [max(nums[i] + dp[0][j - 1], dp[1][j]) for j in range(k)] + [0]

或者可以用采用更一般性的写法:

class Solution:
    def maxSizeSlices(self, nums: List[int]) -> int:
        n = len(nums)
        k = n // 3
        
        def cal(l, r):
            dp = [[0] * (k + 1) for _ in range(2)]
            for i in range(l, r):
                for j in range(k, 0, -1):
                    dp[0][j] = dp[1][j]
                    dp[1][j] = max(dp[1][j], dp[0][j - 1] + nums[i])
            return dp[1][k]
        return max(cal(0, n - 1), cal(1, n))

其实质就是计算顺序的考虑。

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

毕业论文引言 随着计算机技术的发展与普及,计算机已经成为各行业最基本的工具之一,迅速进入千家万户。因此,掌握计算机应用的基本技能成为新世纪人才不可缺少的基本素质之一。为使计算机能正常工作, 除了构成计算机各个组成部分的物理设备外, 一般说来, 还必须要有指挥计算机“做什么”“如何做”的“程序”。程序及其有关文档构成计算机软件, 其中用以书写计算机软件的语言称为计算机程序设计语言。 1 计算机程序设计语言简介 计算机程序设计语言是计算机可以识别的语言,用于描述解决问题的方法,供计算机阅读执行,通常简称为编程语言,是一组用来定义计算机程序的语法规则。它是一种被标准化的交流技巧,用来向计算机发出指令。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。使用程序设计语言往往使程序员能够比使用机器语言更准确地表达他们所想表达的目的。对那些从事计算机科学的人来说,懂得程序设计语言是十分重要的,因为所有的程序都需要程序设计语言才能完成,而计算机的工作是用程序来控制的,离开了程序,计算机将一事无成。 2 开发背景及意义 现有计算器不能计算表达,这是一个缺陷,为此,开发了一个能直接计算表达计算器,这为计算提高了更大的方便,可以大幅度提高计算效率。 第二章 第三章 第一节 递归下降法的描述 3.1.1实现思想 它的主要原理是,对每个非终极符按其产生结构构造相应语法分析子程序,其中终极符产生匹配命令,而非终极符则产生过程调用命令。因为文法递归相应子程序也递归,所以称这种方法为递归子程序下降法或递归下降法。其中子程序的结构与产生结构几乎是一致的。文法中每个非终结符对应一个递归过程(子程序),每个过程的功能是识别由该非终结符推出的串,当某非终结符的产生有多个候选时能够按LL(1)可唯一地确定选择某个候选进行推导。 3.1.2算法的特点 递归下降法是语法分析中最易懂的一种方法。递归下降法要满足的条件:假设A的全部产生为Aα1|α2|……|αn ,则必须满足如下条件才能保证可以唯一的选择合适的产生 predict(Aαi)∩predict(Aαj)=Φ,当i≠j. 3.1.3构造递归下降语法分析程序 采用了递归子程序方法进行语法分析,对文法中的每个非终极符号按其产生结构产生相应的语法分析子程序,完成相应的识别任务。其中终结符产生匹配命令,非终结符则产生调用命令。每次进入子程序之前都预先读入一个单词。因为使用了递归下降方法,所以程序结构层次清晰明了,易于手工实现,且时空效率较高。实际的语法分析工作,从调用总程序的分析子程序开始,根据产生进行递归调用各个分析子程序。 第二节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值