813. 最大平均值和的分组(前缀和 + 递归+记忆化搜索)

Problem: 813. 最大平均值和的分组

文章目录

  • 思路
  • 解题方法
  • Code

思路

首先由于子数组内是连续的,可以用前缀和先把和求出来,再利用perSum[j] -perSum[i-1]来求子数组的和,进而得到区间分组的平均值。现在最大的问题是分组,怎么分组([0,k]) ? 我们将大问题分解成k个小问题,大问题是如何将给定数组 nums 分成最多 k 个非空连续子数组,以使得这些子数组的平均值之和最大。小问题可以定义为:从数组的某个特定起点 i 开始,将数组的剩余部分(nums[i] 到 nums[n-1])分成最多 j 个连续子数组,以使得这些子数组的平均值之和最大,其中 j ≤ k。

解题方法

  1. 计算前缀和
    我们首先计算数组 nums 的前缀和数组 perSum。对于数组中的每个元素 nums[i],perSum[i] 存储从 nums[0] 到 nums[i] 的总和。

  2. 定义记忆化数组
    使用一个二维数组 f 来存储已经解决的子问题的结果。这里 f[i][j] 表示从第 i 个元素开始,将数组剩余部分分成 j 组所能获得的最大分数。如果一个子问题已经被解决,我们可以直接从 f 中取得结果,而无需重新计算。

  3. 编写dfs函数
    递归函数 dfs(i, k) 计算从索引 i 开始,将数组剩余部分分成 k 组所能获得的最大分数。 如果我们到达数组的末尾(即 i == n),则返回 0,因为没有剩余元素可以分组。 如果只剩下一组(即 k == 1),则直接返回从 i 到数组末尾的所有元素的平均值。 如果 f[i][k] 已经有值,则直接返回该值,表示这个子问题已经被解决。 否则,我们遍历所有可能的分割点,计算将数组分成两部分(当前部分和剩余部分)所能获得的最大分数,并更新 f[i][k]

Code

class Solution {
public:
    // dfs 将问题规模缩小,dfs(i,k) 表示在当前位置i到n ,划分成k个分组的平均值
    double dfs(vector<int> &perSum , int i , int k , int n ,vector<vector<double>>& f ) {
        // 已经分完了,返回0 
        if(i == n) return  0 ; 
        // 当前只剩下一组了,就返回它,从当前[i,n]的平均值
        if(k == 1 ) return (perSum[n] - perSum[i] ) *1.0/ (n-i);
        if(f[i][k] > 0 ) return f[i][k] ; 

        double ans = 0.0 ; 
        for(int j = i ; j< n ; j++) {
            // 本组选取了[i,j]区间的平均值 +  下一个组应该是从[j,n]继续分组
            double t =  (perSum[j+1] - perSum[i])*1.0/ (j-i+1) + dfs(perSum , j+1,k-1,n,f) ; 
            ans = max(ans, t) ; 
        }
        f[i][k] = ans ; 
        return ans  ; 
    }
    double largestSumOfAverages(vector<int>& nums, int k) {
        int n = nums.size() ; 
        vector<int> s(n+1) ; 
        // f函数用来存储记忆化搜索结果,前i个元素并且分成 j个组时的最大平均值
        vector<vector<double>> f(n,vector<double>(k+1,0)) ; 
        for(int i = 0 ; i<n ; i++ ) s[i+1]  = s[i] + nums[i] ; 
        return dfs(s,0,k,n,f) ; 


    }
};

你可能感兴趣的:(LeetCode,leetcode,递归,前缀和)