【LeetCode】813. 最大平均值和的分组

题目描述

给定数组 nums 和一个整数 k 。我们将给定的数组 nums 分成 最多 k 个相邻的非空子数组 。 分数 由每个子数组内的平均值的总和构成。
注意我们必须使用 nums 数组中的每一个数进行分组,并且分数不一定需要是整数。
返回我们所能得到的最大 分数 是多少。答案误差在 10-6 内被视为是正确的。

示例 1:

输入: nums = [9,1,2,3,9], k = 3
输出: 20.00000
解释:
nums 的最优分组是[9], [1, 2, 3], [9]. 得到的分数是 9 + (1 + 2 + 3) / 3 + 9 = 20.
我们也可以把 nums 分成[9, 1], [2], [3, 9].
这样的分组得到的分数为 5 + 2 + 6 = 13, 但不是最大值.

示例 2:

输入: nums = [1,2,3,4,5,6,7], k = 4
输出: 20.50000

提示:

1 <= nums.length <= 100
1 <= nums[i] <= 104
1 <= k <= nums.length

方法一:维护前缀和 + 序列DP

class Solution {
public:
    double largestSumOfAverages(vector<int>& nums, int k) {
        int n = nums.size();
        // 前缀和数组 
        vector<double> sum(n + 5);
        for(int i=1; i<=n; i++){
            sum[i] = sum[i-1] + nums[i-1];
        }
        vector<vector<double>> score(n + 10, vector<double>(k + 10));

        for(int i=1; i<=n; i++){
            for(int j=1; j<=min(i, k); j++){
                if(j == 1){
                    // 分为1份
                    score[i][1] = sum[i] / i;
                }
                else{
                    // 分为多份,递推考虑
                    // m是指最后一个子数组的起点,取2和j的最大值
                    // 因为j是当前最少需要的份数
                    // eg:i=4,j=3 如果此时m=2,没有意义,
                    // 2如果是最后一个子数组的起点,那么最多只能分为2份
                    for(int m=max(2, j); m<=i; m++){
                        score[i][j] = max(score[i][j], score[m-1][j-1] + (sum[i] - sum[m-1]) / (double)(i - m + 1));                        
                    }
                }
            }
        }


        return score[n][k];
    }
};

心得
这道题一开始没注意到 邻近 两个字,因此我一开始的思路是错的 「将数组 nums sort降序排序,前 k-1 个元素各占一份, 后面的元素作为第 k 份」,由于忽略了 『邻近』的条件,这个方法只能通过大约一半的测试点。

方法:前缀和 + 序列DP

  • 题意可以理解为:「将 n 个元素划分为最多 m 个连续段,最大化连续段的平均值之和」。

  • 为了方便,令所有数组的下标从 1 开始。
    定义 「 f[i][j] 为考虑将前 i 个元素划分为 j 份的最大平均和 」,答案为 f[n][k] ,其中 1 <= k <= m。

  • 接下来考虑 f[i][j] 的计算,由于划分出来的子数组不能是空集,因此可以根据 j 的大小分情况讨论:

    • 当 j = 1 时,
      此时 f[i][j] = (∑ nums[inx- 1]) / i;
    • 当 j > 1 时,此时枚举最后一个子数组的起点 k ,其中 2 <= k <= i,此时有平均值之和为:
      在这里插入图片描述
      最终 f[i][j] 为枚举所有k 值的最大值。
  • 其中 求解连续段之和可以用 前缀和 进行优化。同时,想要简化代码,还可以利用一个简答的数学结论:「 划分份数越多,平均值之和越大,因此想要取得最大值必然是恰好划分成 m 份」。

你可能感兴趣的:(LeetCode刷题,leetcode,算法,职场和发展)