【每日一题Day40】LC813最大平均值的和分组 | 序列DP

简介: 给定数组 nums 和一个整数 k 。我们将给定的数组 nums 分成 最多 k 个相邻的非空子数组 。 分数 由每个子数组内的平均值的总和构成。

最大平均值的和分组【LC813】


You are given an integer array nums and an integer k. You can partition the array into at most k non-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-6 of the actual answer will be accepted.


给定数组 nums 和一个整数 k 。我们将给定的数组 nums 分成 最多 k 个相邻的非空子数组 。 分数 由每个子数组内的平均值的总和构成。


注意我们必须使用 nums 数组中的每一个数进行分组,并且分数不一定需要是整数。


返回我们所能得到的最大 分数 是多少。答案误差在 10-6 内被视为是正确的。


隔离第4天…

学习使我忘记时间…


序列dp


  • 思路:将 n 个元素划分为k个连续子数组,最大化子数组的平均值之和。


1.确定dp数组(dp table)以及下标的含义


dp[i][j]:将长度为[0,i]的nums分割为j个连续子数组时,得到的最大分数


2.确定递推公式


当j≤k时,nums[i]有两种选择,自成一组,或者并入前一组


。自成一组:dp[i][j]=dp[i−1][j−1]+num[i]


。并入前一组:对于每个nums[i],需要枚举前一组所有可能的初始位置,为了方便计算平均值,使用preSum数组计算前缀和,由于每个子数组至少包含一个元素,因此start∈[1,i)


dp[i][j]=dp[start−1][j−1]+ave(nums[start,i])


。以上两个公式可合并为一个:当start=i时,新的一组只有nums[i]一个元素


dp[i][j]=max(dp[i][j],dp[start−1][j−1]+ave(nums[start,i]))


start∈[1,i]


。preSum[i]为前i−1个数组元素之和,因此连续子数组nums[start,i]的平均值可表示为


ave=(preSum[i+1]−preSum[start−1])/(i−start+1)


3.dp数组如何初始化


特殊处理只分为一组时的dp[i][1]


。dp[0][1]=nums[0]

。dp[1][1]=(nums[0]+nums[1])/2

。dp[i][1]=(nums[0]+nums[1]+……+nums[i])/(i+1)=preSum[i+1]/(i+1)


4.确定遍历顺序


由dp公式可知,外层正序遍历i,内层正序遍历子数组个数j,最后遍历第j个子数组的可能的起始位置start


5.举例推导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 ( n 2 ∗ k )


  • 空间复杂度:O ( n ∗ k )


  • 代码:很丑


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];
    }
}


目录
相关文章
|
6月前
【每日一题Day357】LC1155掷骰子等于目标和的方法数 | dp
【每日一题Day357】LC1155掷骰子等于目标和的方法数 | dp
56 0
|
6月前
【每日一题Day289】LC1749任意子数组和的绝对值的最大值 | dp
【每日一题Day289】LC1749任意子数组和的绝对值的最大值 | dp
46 0
|
6月前
【每日一题Day133】LC2373矩阵中的局部最大值 | 模拟
【每日一题Day133】LC2373矩阵中的局部最大值 | 模拟
49 0
|
6月前
【每日一题Day278】LC2500删除每行中的最大值 | 排序+模拟
【每日一题Day278】LC2500删除每行中的最大值 | 排序+模拟
47 0
|
6月前
PTA-求奇数分之一序列前N项和
求奇数分之一序列前N项和
92 0
|
6月前
|
存储
【每日一题Day307】LC56合并区间 | 排序
【每日一题Day307】LC56合并区间 | 排序
42 0
|
6月前
【每日一题Day236】LC2475数组中不等三元组的数目
【每日一题Day236】LC2475数组中不等三元组的数目
37 0
|
6月前
【每日一题Day255】LC2679矩阵中的和 | 排序
【每日一题Day255】LC2679矩阵中的和 | 排序
28 0
|
6月前
【每日一题Day277】LC2569更新数组后处理求和查询 | 线段树
【每日一题Day277】LC2569更新数组后处理求和查询 | 线段树
30 0
华为机试HJ58:输入n个整数,输出其中最小的k个
华为机试HJ58:输入n个整数,输出其中最小的k个