leetcode
leetcode 2751 ~ 2800
K 个不相交子数组的最大能量值

K 个不相交子数组的最大能量值

难度:

标签:

题目描述

You are given a 0-indexed array of integers nums of length n, and a positive odd integer k.

The strength of x subarrays is defined as strength = sum[1] * x - sum[2] * (x - 1) + sum[3] * (x - 2) - sum[4] * (x - 3) + ... + sum[x] * 1 where sum[i] is the sum of the elements in the ith subarray. Formally, strength is sum of (-1)i+1 * sum[i] * (x - i + 1) over all i's such that 1 <= i <= x.

You need to select k disjoint subarrays from nums, such that their strength is maximum.

Return the maximum possible strength that can be obtained.

Note that the selected subarrays don't need to cover the entire array.

 

Example 1:

Input: nums = [1,2,3,-1,2], k = 3
Output: 22
Explanation: The best possible way to select 3 subarrays is: nums[0..2], nums[3..3], and nums[4..4]. The strength is (1 + 2 + 3) * 3 - (-1) * 2 + 2 * 1 = 22.

Example 2:

Input: nums = [12,-2,-2,-2,-2], k = 5
Output: 64
Explanation: The only possible way to select 5 disjoint subarrays is: nums[0..0], nums[1..1], nums[2..2], nums[3..3], and nums[4..4]. The strength is 12 * 5 - (-2) * 4 + (-2) * 3 - (-2) * 2 + (-2) * 1 = 64.

Example 3:

Input: nums = [-1,-2,-3], k = 1
Output: -1
Explanation: The best possible way to select 1 subarray is: nums[0..0]. The strength is -1.

 

Constraints:

  • 1 <= n <= 104
  • -109 <= nums[i] <= 109
  • 1 <= k <= n
  • 1 <= n * k <= 106
  • k is odd.

代码结果

运行时间: 876 ms, 内存: 17.8 MB


/*
题目思路:
1. 计算每个可能的子数组的和并存储在一个二维数组中。
2. 使用Java Stream API来简化动态规划的部分。
3. 遍历所有可能的子数组组合,更新dp数组。
4. 最后,返回dp数组的最大值。
*/

import java.util.stream.IntStream;

public class Solution {
    public int maxEnergy(int[] nums, int k) {
        int n = nums.length;
        int[][] sum = new int[n][n];
        for (int i = 0; i < n; i++) {
            sum[i][i] = nums[i];
            for (int j = i + 1; j < n; j++) {
                sum[i][j] = sum[i][j - 1] + nums[j];
            }
        }

        int[][] dp = new int[n + 1][k + 1];
        IntStream.range(1, n + 1).forEach(i -> {
            IntStream.range(1, k + 1).forEach(j -> {
                IntStream.range(0, i).forEach(l -> {
                    int energy = IntStream.range(0, j).map(m -> (int) (Math.pow(-1, m + 1) * sum[l][i - 1] * (j - m))).sum();
                    dp[i][j] = Math.max(dp[i][j], dp[l][j - 1] + energy);
                });
            });
        });
        return dp[n][k];
    }
}

解释

方法:

本题解采用动态规划的方式来求解。首先,计算数组 `nums` 的累加和 `acc`,这是为了快速计算任意子数组的和。然后,定义动态规划数组 `dps`,其中 `dps[i]` 表示在数组前 `i` 个元素中选取 `ck` 个子数组能够获得的最大能量值。对于每一个可能的 `k`,计算对应的权重 `cw`,这个权重根据 `k` 的值和当前子数组的索引 `ck` 而有所不同,具体为 `(-1)^ck * (k - ck)`。接着,遍历数组使用滚动数组方式更新 `dps`,每次考虑加入新的子数组时,更新 `dps[i]` 为包括新子数组的最大能量值。这个过程中,我们需要维护当前的最大值 `curs` 以便快速更新 `dps`。最终 `dps[-1]` 将包含在整个数组中选取 `k` 个子数组的最大能量值。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
在你的解决方案中,为什么选择使用动态规划方法来处理这个问题?
动态规划是解决此类问题的有效方法,因为它能够将问题分解成较小的子问题,并通过解决这些子问题来构建最终解决方案。特别是在涉及到多个选择和决策步骤的问题中,如在数组中选择不相交子数组以最大化某种指标(本题中的能量值)时,动态规划可以有效地避免重复计算并储存中间结果,从而降低问题的复杂度。此外,动态规划通过迭代更新解决方案,能够确保每一步都是基于前一步的最优解,这使得最终得到的是全局最优解。
🦆
动态规划数组 `dps[i]` 是如何确保不同子数组之间不重叠的?
在算法的实现中,`dps[i]` 记录的是从数组的开始到第 `i` 个元素,最多取 `ck` 个子数组时的最大能量值。通过逐步增长子数组的数量并更新 `dps[i]`,在每一步中,算法都会检查前一个最优结果和当前可能加入的子数组的组合。这里的关键在于,每次更新 `dps[i]` 时,都是基于在第 `i` 个元素之前的数组段的最优解进行更新。因此,新增的子数组始终是从前一个子数组结束之后的位置开始,这自然地保证了子数组之间不会有重叠。
🦆
你在计算权重 `cw` 时使用了 `(-1)^ck * (k - ck)`,这样的计算方式是如何来的?它与题目描述中的能量值计算有什么联系?
权重 `cw` 的计算方式是为了根据子数组的数量调整每个子数组对总能量值的贡献。这里的计算公式 `(-1)^ck * (k - ck)` 是基于子数组数量来调整权重的正负和大小。当 `ck` (当前子数组的数量)是奇数时,权重为负,这可能代表在某些情境下需要减少由于选择更多子数组带来的额外成本或惩罚。这种权重调整方式确保算法在增加子数组数量时能够平衡每个子数组的贡献,从而在满足题目要求的同时最大化总能量值。在整个解决方案的上下文中,这样的权重调整帮助算法在不同的选择中找到最优解。

相关问题