最大平均值的和分组【LC813】
You are given an integer array
numsand an integerk. You can partition the array into at mostknon-empty adjacent subarrays. The score of a partition is the sum of the averages of each subarray.Note that the partition must use every integer in
nums, and that the score is not necessarily an integer.Return the maximum score you can achieve of all the possible partitions. Answers within
10-6of the actual answer will be accepted.
给定数组
nums和一个整数k。我们将给定的数组nums分成 最多k个相邻的非空子数组 。 分数 由每个子数组内的平均值的总和构成。注意我们必须使用
nums数组中的每一个数进行分组,并且分数不一定需要是整数。返回我们所能得到的最大 分数 是多少。答案误差在
10-6内被视为是正确的。
隔离第4天…
学习使我忘记时间…
序列dp
- 思路:将 n 个元素划分为k个连续子数组,最大化子数组的平均值之和。
-
确定dp数组(dp table)以及下标的含义
dp[i][j]:将长度为[0,i]的nums分割为j个连续子数组时,得到的最大分数
-
确定递推公式
当j≤kj\le kj≤k时,nums[i]有两种选择,自成一组,或者并入前一组
-
自成一组:dp[i][j]=dp[i−1][j−1]+num[i]dp[i][j] = dp[i-1][j-1] + num[i]dp[i][j]=dp[i−1][j−1]+num[i]
-
并入前一组:对于每个nums[i],需要枚举前一组所有可能的初始位置,为了方便计算平均值,使用preSum数组计算前缀和,由于每个子数组至少包含一个元素,因此start∈[1,i)start\in[1,i)start∈[1,i)
dp[i][j]=dp[start−1][j−1]+ave(nums[start,i])dp[i][j]=dp[start-1][j-1] + ave(nums[start,i])dp[i][j]=dp[start−1][j−1]+ave(nums[start,i])
-
以上两个公式可合并为一个:当start=istart=istart=i时,新的一组只有nums[i]nums[i]nums[i]一个元素
dp[i][j]=max(dp[i][j],dp[start−1][j−1]+ave(nums[start,i])) dp[i][j] = max(dp[i][j],dp[start-1][j-1]+ave(nums[start,i])) dp[i][j]=max(dp[i][j],dp[start−1][j−1]+ave(nums[start,i]))start∈[1,i] start\in[1,i] start∈[1,i]
-
preSum[i]preSum[i]preSum[i]为前i−1i-1i−1个数组元素之和,因此连续子数组nums[start,i]nums[start,i]nums[start,i]的平均值可表示为
ave=(preSum[i+1]−preSum[start−1])/(i−start+1) ave=(preSum[i+1] - preSum[start-1])/(i-start+1) ave=(preSum[i+1]−preSum[start−1])/(i−start+1)
-
-
dp数组如何初始化
特殊处理只分为一组时的dp[i][1]
- dp[0][1]=nums[0]dp[0][1] = nums[0]dp[0][1]=nums[0]
- dp[1][1]=(nums[0]+nums[1])/2dp[1][1] = (nums[0] + nums[1])/2dp[1][1]=(nums[0]+nums[1])/2
- dp[i][1]=(nums[0]+nums[1]+……+nums[i])/(i+1)=preSum[i+1]/(i+1)dp[i][1] = (nums[0] + nums[1] + ……+nums[i])/(i+1) = preSum[i+1]/(i+1)dp[i][1]=(nums[0]+nums[1]+……+nums[i])/(i+1)=preSum[i+1]/(i+1)
-
确定遍历顺序
由dp公式可知,外层正序遍历i,内层正序遍历子数组个数j,最后遍历第j个子数组的可能的起始位置start
-
举例推导dp数组
-
代码 :
- 下标均从1开始遍历,代码较清晰
- dp[i+1][j]:将长度为[0,i]的nums分割为j个连续子数组时,得到的最大分数
- preSum[i+1]对应前i个nums
- 只有数组元素大于等于j时才有可能划分为j个连续子数组
class Solution { public double largestSumOfAverages(int[] nums, int k) { int len = nums.length; double[][] dp = new double[len + 1][k + 1]; double[] preSum = new double[len + 1]; for (int i = 1; i <= len; i++){ preSum[i] = preSum[i-1] + nums[i-1]; } for (int i = 1; i <= len; i++){ dp[i][1] = preSum[i] / i; } for (int i = 1; i <= len; i++){ for (int j = 2; j <= k && j <= i; j++){ // 自成一组 与start = i 合并 // dp[i][j] = Math.max(dp[i][j],dp[i-1][j-1] + nums[i-1]); // 并入前一组nums[start,i] for (int start = 2; start <= i; start++){ double ave = (preSum[i] - preSum[start - 1]) / (i - start + 1); dp[i][j] = Math.max(dp[i][j],dp[start - 1][j - 1] + ave); } } } return dp[len][k]; } }-
复杂度
- 时间复杂度:O(n2∗k)O(n^2*k)O(n2∗k)
- 空间复杂度:O(n∗k)O(n*k)O(n∗k)
- 下标均从1开始遍历,代码较清晰
-
代码:很丑
dp[i][j]:将长度为[0,i]的nums分割为j个连续子数组时,得到的最大分数
class Solution { public double largestSumOfAverages(int[] nums, int k) { int len = nums.length; double[][] dp = new double[len][k + 1]; double[] preSum = new double[len + 1]; for (int i = 0; i < len; i++){ preSum[i+1] = preSum[i] + nums[i]; } for (int i = 0; i < len; i++){ dp[i][1] = preSum[i + 1] / (i+1); } for (int i = 0; i < len; i++){ for (int j = 2; j <= k && j <= i + 1; j++){ // 自成一组 与start = i 合并 // dp[i][j] = Math.max(dp[i][j],dp[i-1][j-1] + nums[i-1]); // 并入前一组nums[start,i] for (int start = 1; start <= i; start++){ double ave = (preSum[i + 1] - preSum[start]) / (i - start + 1); dp[i][j] = Math.max(dp[i][j],dp[start - 1][j - 1] + ave); } } } return dp[len - 1][k]; } }

博客主要介绍了如何使用动态规划解决将数组划分为最多k个连续子数组,以最大化子数组平均值之和的问题。通过建立dp数组和预处理前缀和,遍历数组元素和子数组个数,找到最优解。代码实现中涉及到双层循环和最大值比较,最终返回dp[len][k]作为答案。
1500

被折叠的 条评论
为什么被折叠?



