【LeetCode 每日一题】1043. 分隔数组以得到最大和

191 阅读1分钟

1043. 分隔数组以得到最大和

难度:中等

时间:2023/04/19


给你一个整数数组 arr,请你将该数组分隔为长度 最多 为 k 的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。

返回将数组分隔变换后能够得到的元素最大和。本题所用到的测试用例会确保答案是一个 32 位整数。

示例 1:

输入:arr = [1,15,7,9,2,5,10], k = 3
输出:84
解释:数组变为 [15,15,15,9,10,10,10]

示例 2:

输入:arr = [1,4,1,5,7,3,6,1,9,9,3], k = 4
输出:83

示例 3:

输入:arr = [1], k = 1
输出:1

提示:

  • 1 <= arr.length <= 500
  • 0 <= arr[i] <= 10^9
  • 1 <= k <= arr.length

解题思路:

  • 题意 给定数组A,将数组分割成若干个子数组,每个子数组的长度不超过K,并且子数组中的每个数字都变为其中的最大值。问整个数组的最大值之和是多少?

  • 思路 分割的位置会影响之后的可分割的位置,这是一个动态规划问题。

设dp[i]表示前i个元素能达到的最大和。考虑到长度最大为K,那么dp[i]可以由dp[i-K] ~ dp[i-1]推导而来,即状态转移为:

dp[i] = max( dp[j] + max(A[j:i]) * (i - j) ),其中i - K <= j <= i - 1

PS.一开始我想着把区间最大值预处理出来,但时间复杂度是O(N^2)。后来发现这一步是没有必要的,因为通过将对j的遍历方向变为逆序,那么最大值可以同时求出来,时间复杂度是O(NK),很明显后者更好。

class Solution {
public:
    int maxSumAfterPartitioning(vector<int>& A, int K) {
        const int n = A.size();
        // dp[i] : 以nums[i]为切割的结尾,目前切割所能得到的最大值为dp[i]
        vector<int> dp(n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            int maxN = -1;
            for (int j = i - 1; j >= max(i - K, 0); --j) {
                maxN = max(maxN, A[j]);
                dp[i] = max(dp[i], dp[j] + maxN * (i - j));
            }
        }
​
        return dp[n];
    }
};