comments | difficulty | edit_url | rating | source | tags | ||
---|---|---|---|---|---|---|---|
true |
中等 |
1846 |
第 403 场周赛 Q3 |
|
给你一个长度为 n
的整数数组 nums
。
子数组 nums[l..r]
(其中 0 <= l <= r < n
)的 成本 定义为:
cost(l, r) = nums[l] - nums[l + 1] + ... + nums[r] * (−1)r − l
你的任务是将 nums
分割成若干子数组,使得所有子数组的成本之和 最大化,并确保每个元素 正好 属于一个子数组。
具体来说,如果 nums
被分割成 k
个子数组,且分割点为索引 i1, i2, ..., ik − 1
(其中 0 <= i1 < i2 < ... < ik - 1 < n - 1
),则总成本为:
cost(0, i1) + cost(i1 + 1, i2) + ... + cost(ik − 1 + 1, n − 1)
返回在最优分割方式下的子数组成本之和的最大值。
注意:如果 nums
没有被分割,即 k = 1
,则总成本即为 cost(0, n - 1)
。
示例 1:
输入: nums = [1,-2,3,4]
输出: 10
解释:
一种总成本最大化的方法是将 [1, -2, 3, 4]
分割成子数组 [1, -2, 3]
和 [4]
。总成本为 (1 + 2 + 3) + 4 = 10
。
示例 2:
输入: nums = [1,-1,1,-1]
输出: 4
解释:
一种总成本最大化的方法是将 [1, -1, 1, -1]
分割成子数组 [1, -1]
和 [1, -1]
。总成本为 (1 + 1) + (1 + 1) = 4
。
示例 3:
输入: nums = [0]
输出: 0
解释:
无法进一步分割数组,因此答案为 0。
示例 4:
输入: nums = [1,-1]
输出: 2
解释:
选择整个数组,总成本为 1 + 1 = 2
,这是可能的最大成本。
提示:
1 <= nums.length <= 105
-109 <= nums[i] <= 109
根据题目描述,如果当前数没取反,那么下一个可以取反,也可以不取反;如果当前数取反了,那么下一个只能不取反。
因此,我们定义一个函数
函数
- 如果
$i \geq \textit{len}(nums)$ ,表示已经遍历完了数组,返回$0$ ; - 否则,第
$i$ 个数可以不取反,此时答案为$nums[i] + \textit{dfs}(i + 1, 1)$ ;如果$j = 1$ ,表示第$i$ 个数可以取反,此时答案为$\max(\textit{dfs}(i + 1, 0) - nums[i])$ 。我们取两者的最大值即可。
为了避免重复计算,我们可以使用记忆化搜索,将已经计算过的结果保存起来。
时间复杂度
class Solution:
def maximumTotalCost(self, nums: List[int]) -> int:
@cache
def dfs(i: int, j: int) -> int:
if i >= len(nums):
return 0
ans = nums[i] + dfs(i + 1, 1)
if j == 1:
ans = max(ans, -nums[i] + dfs(i + 1, 0))
return ans
return dfs(0, 0)
class Solution {
private Long[][] f;
private int[] nums;
private int n;
public long maximumTotalCost(int[] nums) {
n = nums.length;
this.nums = nums;
f = new Long[n][2];
return dfs(0, 0);
}
private long dfs(int i, int j) {
if (i >= n) {
return 0;
}
if (f[i][j] != null) {
return f[i][j];
}
f[i][j] = nums[i] + dfs(i + 1, 1);
if (j == 1) {
f[i][j] = Math.max(f[i][j], -nums[i] + dfs(i + 1, 0));
}
return f[i][j];
}
}
class Solution {
public:
long long maximumTotalCost(vector<int>& nums) {
int n = nums.size();
long long f[n][2];
fill(f[0], f[n], LLONG_MIN);
auto dfs = [&](this auto&& dfs, int i, int j) -> long long {
if (i >= n) {
return 0;
}
if (f[i][j] != LLONG_MIN) {
return f[i][j];
}
f[i][j] = nums[i] + dfs(i + 1, 1);
if (j) {
f[i][j] = max(f[i][j], -nums[i] + dfs(i + 1, 0));
}
return f[i][j];
};
return dfs(0, 0);
}
};
func maximumTotalCost(nums []int) int64 {
n := len(nums)
f := make([][2]int64, n)
for i := range f {
f[i] = [2]int64{-1e18, -1e18}
}
var dfs func(int, int) int64
dfs = func(i, j int) int64 {
if i >= n {
return 0
}
if f[i][j] != -1e18 {
return f[i][j]
}
f[i][j] = int64(nums[i]) + dfs(i+1, 1)
if j > 0 {
f[i][j] = max(f[i][j], int64(-nums[i])+dfs(i+1, 0))
}
return f[i][j]
}
return dfs(0, 0)
}
function maximumTotalCost(nums: number[]): number {
const n = nums.length;
const f: number[][] = Array.from({ length: n }, () => Array(2).fill(-Infinity));
const dfs = (i: number, j: number): number => {
if (i >= n) {
return 0;
}
if (f[i][j] !== -Infinity) {
return f[i][j];
}
f[i][j] = nums[i] + dfs(i + 1, 1);
if (j) {
f[i][j] = Math.max(f[i][j], -nums[i] + dfs(i + 1, 0));
}
return f[i][j];
};
return dfs(0, 0);
}
我们可以将方法一中的记忆化搜索转换为动态规划。
定义
遍历数组
- 如果当前数不取反,那么
$f$ 的值为$\max(f, g) + x$ ,表示当前数不取反,那么前一个数可以取反或不取反; - 如果当前数取反,那么
$g$ 的值为$f - x$ ,表示当前数取反,那么前一个数不能取反
最终答案为
时间复杂度
class Solution:
def maximumTotalCost(self, nums: List[int]) -> int:
f, g = -inf, 0
for x in nums:
f, g = max(f, g) + x, f - x
return max(f, g)
class Solution {
public long maximumTotalCost(int[] nums) {
long f = Long.MIN_VALUE / 2, g = 0;
for (int x : nums) {
long ff = Math.max(f, g) + x;
long gg = f - x;
f = ff;
g = gg;
}
return Math.max(f, g);
}
}
class Solution {
public:
long long maximumTotalCost(vector<int>& nums) {
long long f = LLONG_MIN / 2, g = 0;
for (int x : nums) {
long long ff = max(f, g) + x, gg = f - x;
f = ff;
g = gg;
}
return max(f, g);
}
};
func maximumTotalCost(nums []int) int64 {
f, g := math.MinInt64/2, 0
for _, x := range nums {
f, g = max(f, g)+x, f-x
}
return int64(max(f, g))
}
function maximumTotalCost(nums: number[]): number {
let [f, g] = [-Infinity, 0];
for (const x of nums) {
[f, g] = [Math.max(f, g) + x, f - x];
}
return Math.max(f, g);
}