Medium
题目描述
给你一个整数数组 nums,当 nums 至少包含 2 个元素时,你可以执行以下任一操作:
- 选择
nums的前两个元素并删除它们。 - 选择
nums的后两个元素并删除它们。 - 选择
nums的第一个和最后一个元素并删除它们。
操作的分数是被删除元素的和。
你的任务是找到可以执行的最大操作数,使得所有操作都有相同的分数。
返回满足上述条件的最大可能操作数。
示例 1:
输入:nums = [3,2,1,2,3,4]
输出:3
解释:我们执行以下操作:
- 删除前两个元素,分数为 3 + 2 = 5,nums = [1,2,3,4]
- 删除第一个和最后一个元素,分数为 1 + 4 = 5,nums = [2,3]
- 删除第一个和最后一个元素,分数为 2 + 3 = 5,nums = []
由于 nums 为空,无法执行更多操作。
示例 2:
输入:nums = [3,2,6,1,4]
输出:2
解释:我们执行以下操作:
- 删除前两个元素,分数为 3 + 2 = 5,nums = [6,1,4]
- 删除后两个元素,分数为 1 + 4 = 5,nums = [6]
可以证明最多能执行 2 次操作。
约束条件:
2 <= nums.length <= 20001 <= nums[i] <= 1000
解题思路
这是一道动态规划问题,核心思路如下:
关键观察:第一次操作确定了所有后续操作的分数。因为题目要求所有操作必须有相同的分数,所以我们只需要考虑三种可能的分数:
nums[0] + nums[1](删除前两个)nums[n-2] + nums[n-1](删除后两个)nums[0] + nums[n-1](删除首尾)
动态规划设计:对于每个可能的分数,使用
dp[l][r]表示在子数组nums[l...r]上能执行的最大操作数。状态转移:对于区间
[l, r],尝试三种操作:- 删除
nums[l], nums[l+1]:如果和等于目标分数,则dp[l][r] = 1 + dp[l+2][r] - 删除
nums[r-1], nums[r]:如果和等于目标分数,则dp[l][r] = 1 + dp[l][r-2] - 删除
nums[l], nums[r]:如果和等于目标分数,则dp[l][r] = 1 + dp[l+1][r-1]
- 删除
边界条件:当
l >= r时,无法进行操作,返回 0。
使用记忆化搜索来避免重复计算,最终答案是三种可能分数下的最大操作数。
代码实现
class Solution {
public:
int maxOperations(vector<int>& nums) {
int n = nums.size();
function<int(int, int, int, vector<vector<int>>&)> dfs = [&](int l, int r, int target, vector<vector<int>>& memo) -> int {
if (l >= r) return 0;
if (memo[l][r] != -1) return memo[l][r];
int res = 0;
// 删除前两个
if (l + 1 <= r && nums[l] + nums[l + 1] == target) {
res = max(res, 1 + dfs(l + 2, r, target, memo));
}
// 删除后两个
if (l <= r - 1 && nums[r - 1] + nums[r] == target) {
res = max(res, 1 + dfs(l, r - 2, target, memo));
}
// 删除首尾
if (nums[l] + nums[r] == target) {
res = max(res, 1 + dfs(l + 1, r - 1, target, memo));
}
return memo[l][r] = res;
};
int ans = 0;
// 尝试三种可能的分数
vector<int> targets = {nums[0] + nums[1], nums[n-2] + nums[n-1], nums[0] + nums[n-1]};
for (int target : targets) {
vector<vector<int>> memo(n, vector<int>(n, -1));
ans = max(ans, dfs(0, n - 1, target, memo));
}
return ans;
}
};
class Solution:
def maxOperations(self, nums: List[int]) -> int:
n = len(nums)
def dfs(l, r, target, memo):
if l >= r:
return 0
if memo[l][r] != -1:
return memo[l][r]
res = 0
# 删除前两个
if l + 1 <= r and nums[l] + nums[l + 1] == target:
res = max(res, 1 + dfs(l + 2, r, target, memo))
# 删除后两个
if l <= r - 1 and nums[r - 1] + nums[r] == target:
res = max(res, 1 + dfs(l, r - 2, target, memo))
# 删除首尾
if nums[l] + nums[r] == target:
res = max(res, 1 + dfs(l + 1, r - 1, target, memo))
memo[l][r] = res
return res
ans = 0
# 尝试三种可能的分数
targets = [nums[0] + nums[1], nums[n-2] + nums[n-1], nums[0] + nums[n-1]]
for target in targets:
memo = [[-1] * n for _ in range(n)]
ans = max(ans, dfs(0, n - 1, target, memo))
return ans
public class Solution {
public int MaxOperations(int[] nums) {
int n = nums.Length;
int Dfs(int l, int r, int target, int[,] memo) {
if (l >= r) return 0;
if (memo[l, r] != -1) return memo[l, r];
int res = 0;
// 删除前两个
if (l + 1 <= r && nums[l] + nums[l + 1] == target) {
res = Math.Max(res, 1 + Dfs(l + 2, r, target, memo));
}
// 删除后两个
if (l <= r - 1 && nums[r - 1] + nums[r] == target) {
res = Math.Max(res, 1 + Dfs(l, r - 2, target, memo));
}
// 删除首尾
if (nums[l] + nums[r] == target) {
res = Math.Max(res, 1 + Dfs(l + 1, r - 1, target, memo));
}
return memo[l, r] = res;
}
int ans = 0;
// 尝试三种可能的分数
int[] targets = {nums[0] + nums[1], nums[n-2] + nums[n-1], nums[0] + nums[n-1]};
foreach (int target in targets) {
int[,] memo = new int[n, n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
memo[i, j] = -1;
}
}
ans = Math.Max(ans, Dfs(0, n - 1, target, memo));
}
return ans;
}
}
var maxOperations = function(nums) {
const n = nums.length;
function dfs(l, r, target, memo) {
if (l >= r) return 0;
if (memo[l][r] !== -1) return memo[l][r];
let res = 0;
// 删除前两个
if (l + 1 <= r && nums[l] + nums[l + 1]
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 每种可能的分数需要 O(n²) 时间进行记忆化搜索,共3种可能 |
| 空间复杂度 | O(n²) | 记忆化数组的空间开销 |