目录
1388. 3n 块披萨
问题描述:
实现代码与解析:
动态规划
原理思路:
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 的披萨,这种情况下你的总和不是最大的。
提示:
1 <= slices.length <= 500
slices.length % 3 == 0
1 <= slices[i] <= 1000
实现代码与解析:
动态规划
class Solution {
public:
int dp(vector<int> slices)
{
int n = slices.size();
// f[i][j] 前 i 个披萨中选了 j 个的最大值
vector<vector<int>> f(n, vector<int>((n + 1) / 3 + 1, 0));
f[0][0] = 0; // 第 1 个不拿,大小为0
f[0][1] = slices[0]; // 第一个拿
f[1][0] = 0; // 第 2 个 不拿
f[1][1] = max(slices[0], slices[1]);
for (int i = 2; i < n; i++)
{
f[i][0] = 0; // 一个不选是0
for (int j = 1; j <= (n + 1) / 3; j++)
f[i][j] = max(f[i - 2][j - 1] + slices[i], f[i - 1][j]);
}
return f[n - 1][(n + 1) / 3];
}
int maxSizeSlices(vector<int>& slices) {
vector<int> slices1(slices.begin() + 1, slices.end());
vector<int> slices2(slices.begin(), slices.end() - 1);
int res1 = dp(slices1);
int res2 = dp(slices2);
return max(res1, res2);
}
};
原理思路:
很显然是动态规划的题。
首先,解析题目含义,也就是选取了一块披萨后,其相邻的披萨不能选取。所以题目就转换成,在n块披萨中选取n / 3个不相邻披萨所能获得的最大值,我们最好在考虑环的问题。
dp数组 f [ i ][ j ] 含义:从前 i 个披萨中选了 j 个的最大值;
初始化:因为选取的是不相邻的,所以后一个状态肯定要用到前两个状态,所以我们初始化前两个中的4种状态。
f[0][0] = 0; // 第 1 个不拿,大小为0
f[0][1] = slices[0]; // 第一个拿
f[1][0] = 0; // 第 2 个 不拿
f[1][1] = max(slices[0], slices[1]);
递推式就很好写了:第 i 个不选,那就取 i 时的最大值即可,选取的,就选 i - 2 时的最大值加上当前披萨的大小即可,两种状态取一个最大值。
f[i][j] = max(f[i - 2][j - 1] + slices[i], f[i - 1][j]);
最后就是环的处理,考虑两种情况,一种是只考虑第一个,一种是只考虑最后一个,也是最后两种情况取一个最大值即可。
vector<int> slices1(slices.begin() + 1, slices.end());
vector<int> slices2(slices.begin(), slices.end() - 1);
int res1 = dp(slices1);
int res2 = dp(slices2);
return max(res1, res2);