LeetCode 题解工作台

3n 块披萨

给你一个披萨,它由 3n 块不同大小的部分组成,现在你和你的朋友们需要按照如下规则来分披萨: 你挑选 任意 一块披萨。 Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。 Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。 重复上述过程直到没有披萨剩下。 每一块披萨的大小按顺时针方向由循…

category

4

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

困难 · 状态·转移·动态规划

bolt

答案摘要

我们可以将这个问题转化为:在一个长度为 的环形数组中,选择其中 个不相邻的数,使得这 个数的和最大。 证明如下:

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给你一个披萨,它由 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 的披萨,这种情况下你的总和不是最大的。

 

提示:

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

解题思路

方法一:动态规划

我们可以将这个问题转化为:在一个长度为 3n3n 的环形数组中,选择其中 nn 个不相邻的数,使得这 nn 个数的和最大。

证明如下:

  • n=1n = 1 时,我们可以选择数组中的任意一个数。
  • n>1n \gt 1 时,那么一定存在一个数,使得它的某一侧有两个连续的数没有被选择,而另一侧至少有一个数没有被选择。因此,我们可以将这个数和它两侧的数一起从数组中删除,然后剩下的 3(n1)3(n - 1) 个数构成一个新的环形数组。问题规模缩小成了在长度为 3(n1)3(n - 1) 的环形数组中选择 n1n - 1 个不相邻的数,使得这 n1n - 1 个数的和最大。

因此,我们需要求解的问题可以转化为:在一个长度为 3n3n 的环形数组中,选择其中 nn 个不相邻的数,使得这 nn 个数的和最大。

环形数组中,如果选择了第一个数,那么最后一个数就不能选择,如果选择了最后一个数,那么第一个数就不能选择,因此我们可以将环形数组拆成两个数组,一个是去掉第一个数的,一个是去掉最后一个数的,然后分别求解这两个数组的最大值,最后取两个最大值中的较大值即可。

我们用一个函数 g(nums)g(nums),表示在数组 numsnums 中选择 nn 个不相邻的数,使得这 nn 个数的和最大,那么我们的目标就是求 g(slices)g(slices)g(slices[1:])g(slices[1:]) 中的较大值。

函数 g(nums)g(nums) 的求解方法如下:

我们记数组 numsnums 的长度为 mm,定义 f[i][j]f[i][j] 表示在数组 numsnums 的前 ii 个数中选择 jj 个不相邻的数的最大和。

考虑 f[i][j]f[i][j],如果我们不选择第 ii 个数,那么 f[i][j]=f[i1][j]f[i][j] = f[i - 1][j],如果我们选择第 ii 个数,那么 f[i][j]=f[i2][j1]+nums[i1]f[i][j] = f[i - 2][j - 1] + nums[i - 1],因此我们可以得到状态转移方程:

f[i][j]=max(f[i1][j],f[i2][j1]+nums[i1])f[i][j] = \max(f[i - 1][j], f[i - 2][j - 1] + nums[i - 1])

最后返回 f[m][n]f[m][n] 即可。

时间复杂度 O(n2)O(n^2),空间复杂度 O(n2)O(n^2)。其中 nn 是数组 slicesslices 的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
    def maxSizeSlices(self, slices: List[int]) -> int:
        def g(nums: List[int]) -> int:
            m = len(nums)
            f = [[0] * (n + 1) for _ in range(m + 1)]
            for i in range(1, m + 1):
                for j in range(1, n + 1):
                    f[i][j] = max(
                        f[i - 1][j], (f[i - 2][j - 1] if i >= 2 else 0) + nums[i - 1]
                    )
            return f[m][n]

        n = len(slices) // 3
        a, b = g(slices[:-1]), g(slices[1:])
        return max(a, b)
speed

复杂度分析

指标
时间complexity is O(n^2) due to iterating over slices and possible picks in DP. Space complexity is O(n^2) for the DP table, reducible to O(n) using optimized rolling array techniques.
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Checking if you recognize the circular array pattern.

  • question_mark

    Expecting you to translate circular dependency into two linear DP scenarios.

  • question_mark

    Observing understanding of state transitions for dynamic programming.

warning

常见陷阱

外企场景
  • error

    Attempting a purely greedy solution without considering adjacent slice removal.

  • error

    Ignoring the circular array edge cases when picking the first or last slice.

  • error

    Incorrect DP indexing that does not account for skipping one slice after picking.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Select maximum sum from a linear array where picking one element removes its neighbors.

  • arrow_right_alt

    Maximizing non-adjacent selections from a 2n-length array with fixed number of picks.

  • arrow_right_alt

    Generalized k-consecutive removal problem in a circular array.

help

常见问题

外企场景

3n 块披萨题解:状态·转移·动态规划 | LeetCode #1388 困难