Given an integer array arr
, you should partition the array into (contiguous) subarrays of length at most k
. After partitioning, each subarray has their values changed to become the maximum value of that subarray.
Return the largest sum of the given array after partitioning.
Example 1:
Input: arr = [1,15,7,9,2,5,10], k = 3
Output: 84
Explanation: arr becomes [15,15,15,9,10,10,10]
Example 2:
Input: arr = [1,4,1,5,7,3,6,1,9,9,3], k = 4
Output: 83
Example 3:
Input: arr = [1], k = 1
Output: 1
Constraints:
1 <= arr.length <= 500
0 <= arr[i] <= 109
1 <= k <= arr.length
這道題給了一個數組 arr,和一個正整數k,說是將數組分成若干個長度不超過k的子數組,分割后的子數組所有的數字都變成該子數組中的最大值,讓求分割后的所有子數組數字之和。由於分割的子數組長度不固定,用暴力搜索的話將會有很多很多種情況,不出意外的話會超時。對於這種玩子數組,又是求極值的題,刷題老司機們應該立馬就能想到用動態規划 Dynamic Programming 來做。先來定義 dp 數組,先從最簡單的考慮,使用一個一維的 dp 數組,其中 dp[i] 就表示分割數組中的前i個數字組成的數組可以得到的最大的數字之和。下面來考慮狀態轉移方程怎么求,對於 dp[i] 來說,若把最后k個數字分割出來,那么前i個數字就被分成了兩個部分,前 i-k 個數字,其數字之和可以直接由 dp[i-k] 來取得,后面的k個數字,則需要求出其中最大的數字,然后乘以k,用這兩部分之和來更新 dp[i] 即可。由於題目中說了分割的長度不超過k,那么就是說小於k的也是可以的,則需要遍歷 [1, k] 區間所有的長度,均進行分割。接下來看代碼,建立一個大小為 n+1 的 dp 數組,然后i從1遍歷到n,此時新建一個變量 curMax 記錄當前的最大值,然后用j從1遍歷到k,同時要保證 i-j 是大於等於0的,因為需要前半部分存在,實際上這是從第i個數字開始往前找j個數字,然后記錄其中最大的數字 curMax,並且不斷用 dp[i-j] + curMax * j
來更新 dp[i] 即可,參見代碼如下:
class Solution {
public:
int maxSumAfterPartitioning(vector<int>& arr, int k) {
int n = arr.size();
vector<int> dp(n + 1);
for (int i = 1; i <= n; ++i) {
int curMax = 0;
for (int j = 1; j <= k && i - j >= 0; ++j) {
curMax = max(curMax, arr[i - j]);
dp[i] = max(dp[i], dp[i - j] + curMax * j);
}
}
return dp[n];
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1043
參考資料:
https://leetcode.com/problems/partition-array-for-maximum-sum/