一、题目
1、题目描述
给你一个披萨,它由 3n 块不同大小的部分组成,现在你和你的朋友们需要按照如下规则来分披萨:
- 你挑选 任意 一块披萨。
- Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。
- Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。
- 重复上述过程直到没有披萨剩下。
每一块披萨的大小按顺时针方向由循环数组
slices
表示。请你返回你可以获得的披萨大小总和的最大值。
2、接口描述
python3
class Solution:
def maxSizeSlices(self, slices: List[int]) -> int:
cpp
class Solution {
public:
int maxSizeSlices(vector<int>& slices) {
}
};
3、原题链接
二、解题报告
1、思路分析
我们发现选择一块披萨后,左右相邻的两个就不能选择了,这就跟213. 打家劫舍 II这道经典的题目是类似的
我们定义状态f[i][j]为下标[0, i]的披萨拿了j块的最大总和,那么有状态转移方程:
f[i][j] = max(f[i - 1][j], f[i - 2][j - 1] + slices[i])
方程的含义即拿第i块披萨以及不拿第i块披萨
不拿的话,问题转化为[0, i - 1]中拿j块披萨,拿的话,那就转化为从[0, i - 1]中拿j - 1块的最大总和
由于是环形,对于环形我们通常是倍增,破环成链,但是本题的规则使得我们拿了第0块就拿不了最后一块,拿了最后一块就拿不了第0块,所以在[0, n - 2]的数组上以及[1, n - 1]的数组上跑两次动态规划即可
2、复杂度
时间复杂度: O(n^2)空间复杂度:O(n^2)
3、代码详解
python3
class Solution:
def calc(self, a: List[int]) -> int:
tot, n = len(a), (len(a) + 1) // 3
dp = [[0] * (n + 1) for _ in range(tot)]
dp[0][1], dp[1][1] = a[0], max(a[0], a[1])
for i in range(2, tot):
for j in range(1, n + 1):
dp[i][j] = max(dp[i - 1][j], dp[i - 2][j - 1] + a[i])
return dp[tot - 1][n]
def maxSizeSlices(self, slices: List[int]) -> int:
return max(self.calc(slices[1:]), self.calc(slices[0:-1]))
cpp
class Solution {
public:
int maxSizeSlices(vector<int>& slices) {
function<int(const vector<int>&)> calc = [&](const vector<int>& a){
int tot = a.size(), n = (tot + 1) / 3;
vector<vector<int>> dp(tot, vector<int>(n + 1));
dp[0][1] = a[0], dp[1][1] = max(a[0], a[1]);
for(int i = 2; i < tot; i++)
for(int j = 1; j <= n; j++)
dp[i][j] = max(dp[i - 1][j], dp[i - 2][j - 1] + a[i]);
return dp[tot - 1][n];
};
return max(calc(vector<int>(slices.begin(), slices.end() - 1)), calc(vector<int>(slices.begin() + 1, slices.end())));
}
};