We partition a row of numbers A into at most K adjacent (non-empty) groups, then our score is the sum of the average of each group. What is the largest score we can achieve?

Note that our partition must use every number in A, and that scores are not necessarily integers.

Example:
Input:
A = [9,1,2,3,9]
K = 3
Output: 20
Explanation:
The best choice is to partition A into [9], [1, 2, 3], [9]. The answer is 9 + (1 + 2 + 3) / 3 + 9 = 20.
We could have also partitioned A into [9, 1], [2], [3, 9], for example.
That partition would lead to a score of 5 + 2 + 6 = 13, which is worse.

Note:

  • 1 <= A.length <= 100.
  • 1 <= A[i] <= 10000.
  • 1 <= K <= A.length.
  • Answers within 10^-6 of the correct answer will be accepted as correct.

这道题给了我们一个数组,说是让我们将数组分成至多K个非空组,然后说需要统计的分数是各组的平均数之和,让我们求一个分割方法,使得这个分数值最大,当然这个分数值不一定是整型数。这道题限制了分割的组必须为非空组,那么就是说K值要小于等于数组的元素个数。但是实际上博主感觉这个必须为非空的限制有没有都一样,因为题目中说至多分成K组,也就是说可以根本不分组,那么比如你输入个A=[9,1], K=3,照样返回一个10,给人的感觉好像是分成了[9], [1], [] 这三组一样,但其实只是分成了两组[9] 和 [1]。但我们不必纠结这些,不是重点。没有啥思路的情况下我们就先想想brute force的解法呗,对于题目中给的那个例子,我们用最暴力的方法就是遍历所有的可能性,即遍历所有分割成三个组的情况,用三个for循环。貌似行的通,但问题来了,如果K大于3呢,每大一个,多加一个for循环么,总共K个for循环?如果K=100呢,100个for循环么?画面太美我不敢看!显然这道题用brute force是行不通的,那么换个方法呗!像这种求极值的题,又是玩数组的题,根据老夫行走江湖多年的经验,十有八九都是用Dynamic Programming来做的。玩子数组且跟极值有关的题天然适合用DP来做,想想为什么?DP的本质是什么,不就是状态转移方程,根据前面的状态来更新当前的状态。而子数组不就是整个数组的前一个状态,不停的更新的使得我们最终能得到极值。

好,下面进入正题。DP走起,首先来考虑dp数组的定义,我们如何定义dp数组有时候很关键,定义的不好,那么就无法写出正确的状态转移方程。对于这道题,我们很容易直接用一个一维数组dp,其中dp[i]表示范围为[0, i]的子数组分成三组能得到的最大分数。用这样定义的dp数组的话,状态转移方程将会非常难写,因为我们忽略了一个重要的信息,即K。dp数组不把K加进去的话就不知道当前要分几组,这个Hidden Information是解题的关键。这是DP中比较难的一类,有些DP题的隐藏信息藏的更深,不挖出来就无法解题。这道题的dp数组应该是个二维数组,其中dp[i][k]表示范围是[i, n-1]的子数组分成k组的最大得分。那么这里你就会纳闷了,为啥范围是[i, n-1]而不是[0, i],为啥要取后半段呢,不着急,听博主慢慢道来。由于把[i, n-1]范围内的子数组分成k组,那么suppose我们已经知道了任意范围内分成k-1组的最大分数,这是此类型题目的破题关键所在,要求状态k,一定要先求出所有的状态k-1,那么问题就转换成了从k-1组变成k组,即多分出一组,那么在范围[i, n-1]多分出一组,实际上就是将其分成两部分,一部分是一组,另一部分是k-1组,怎么分,就用一个变量j,遍历范围(i, n-1)中的每一个位置,那么分成的这两部分的分数如何计算呢?第一部分[i, j),由于是一组,那么直接求出平均值即可,另一部分由于是k-1组,由于我们已经知道了所有k-1的情况,可以直接从cache中读出来dp[j][k-1],二者相加即可 avg(i, j) + dp[j][k-1],所以我们可以得出状态转移方程如下:

dp[i][k] = max(avg(i, n) + max_{j > i} (avg(i, j) + dp[j][k-1]))

这里的avg(i, n)是其可能出现的情况,由于是至多分为k组,所以我们可以不分组,所以直接计算范围[i, n-1]内的平均值,然后用j来遍历区间(i, n-1)中的每一个位置,最终得到的dp[i][k]就即为所求。注意这里我们通过建立累加和数组sums来快速计算某个区间之和。博主觉得这道题十分的经典,考察点非常的多,很具有代表性,标为Hard都不过分,前面提到了dp[i][k]表示的是范围[i, n-1]的子数组分成k组的最大得分,现在想想貌似定义为[0, i]范围内的子数组分成k组的最大得分应该也是可以的,那么此时j就是遍历(0, i)中的每个位置了,好像也没什么不妥的地方,有兴趣的童鞋可以尝试的写一下~

解法一:

class Solution {
public:
double largestSumOfAverages(vector<int>& A, int K) {
int n = A.size();
vector<double> sums(n + );
vector<vector<double>> dp(n, vector<double>(K));
for (int i = ; i < n; ++i) {
sums[i + ] = sums[i] + A[i];
}
for (int i = ; i < n; ++i) {
dp[i][] = (sums[n] - sums[i]) / (n - i);
}
for (int k = ; k < K; ++k) {
for (int i = ; i < n - ; ++i) {
for (int j = i + ; j < n; ++j) {
dp[i][k] = max(dp[i][k], (sums[j] - sums[i]) / (j - i) + dp[j][k - ]);
}
}
}
return dp[][K - ];
}
};

我们可以对空间进行优化,由于每次的状态k,只跟前一个状态k-1有关,所以我们不需要将所有的状态都保存起来,只需要保存前一个状态的值就行了,那么我们就用一个一维数组就可以了,不断的进行覆盖,从而达到了节省空间的目的,参见代码如下:

解法二:

class Solution {
public:
double largestSumOfAverages(vector<int>& A, int K) {
int n = A.size();
vector<double> sums(n + );
vector<double> dp(n);
for (int i = ; i < n; ++i) {
sums[i + ] = sums[i] + A[i];
}
for (int i = ; i < n; ++i) {
dp[i] = (sums[n] - sums[i]) / (n - i);
}
for (int k = ; k < K; ++k) {
for (int i = ; i < n - ; ++i) {
for (int j = i + ; j < n; ++j) {
dp[i] = max(dp[i], (sums[j] - sums[i]) / (j - i) + dp[j]);
}
}
}
return dp[];
}
};

我们也可以是用递归加记忆数组的方式来实现,记忆数组的运作原理和DP十分类似,也是一种cache,将已经计算过的结果保存起来,用的时候直接取即可,避免了大量的重复计算,参见代码如下:

解法三:

class Solution {
public:
double largestSumOfAverages(vector<int>& A, int K) {
int n = A.size();
vector<vector<double>> memo(, vector<double>());
double cur = ;
for (int i = ; i < n; ++i) {
cur += A[i];
memo[i + ][] = cur / (i + );
}
return helper(A, K, n, memo);
}
double helper(vector<int>& A, int k, int j, vector<vector<double>>& memo) {
if (memo[j][k] > ) return memo[j][k];
double cur = ;
for (int i = j - ; i > ; --i) {
cur += A[i];
memo[j][k] = max(memo[j][k], helper(A, k - , i, memo) + cur / (j - i));
}
return memo[j][k];
}
};

参考资料:

https://leetcode.com/problems/largest-sum-of-averages/description/

https://leetcode.com/problems/largest-sum-of-averages/solution/

https://leetcode.com/problems/largest-sum-of-averages/discuss/122739/C++JavaPython-Easy-Understood-Solution-with-Explanation

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Largest Sum of Averages 最大的平均数之和的更多相关文章

  1. 【LeetCode】813. Largest Sum of Averages 解题报告(Python)

    [LeetCode]813. Largest Sum of Averages 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博 ...

  2. LC 813. Largest Sum of Averages

    We partition a row of numbers A into at most K adjacent (non-empty) groups, then our score is the su ...

  3. [Swift]LeetCode813. 最大平均值和的分组 | Largest Sum of Averages

    We partition a row of numbers A into at most K adjacent (non-empty) groups, then our score is the su ...

  4. leetcode 813. Largest Sum of Averages

    对于一个数组中的数进行分组,取每个组里的平均值进行加和的. 使用动态规划,其中dp[i][k]表示以i为结尾的有k个分组的,那么递推式为: dp[i][k]=dp[j][k-1]+(sum[i]-su ...

  5. 【leetcode】813. Largest Sum of Averages

    题目如下: 解题思路:求最值的题目优先考虑是否可以用动态规划.记dp[i][j]表示在数组A的第j个元素后面加上第i+1 (i从0开始计数)个分隔符后可以得到的最大平均值,那么可以得到递归关系式: d ...

  6. 813. Largest Sum of Averages

    We partition a row of numbers A into at most K adjacent (non-empty) groups, then our score is the su ...

  7. leetcode813 Largest Sum of Averages

    """ We partition a row of numbers A into at most K adjacent (non-empty) groups, then ...

  8. 动态规划-Largest Sum of Averages

    2018-07-12 23:21:53 问题描述: 问题求解: dp[i][j] : 以ai结尾的分j个部分得到的最大值 dp[i][j] = max{dp[k][j - 1] + (ak+1 + . ...

  9. [LeetCode] 891. Sum of Subsequence Widths 子序列宽度之和

    Given an array of integers A, consider all non-empty subsequences of A. For any sequence S, let the  ...

随机推荐

  1. Java设计模式之原型设计模式

    概述 设计模式(Design Pattern)是一套被反复使用.多数人知晓的.经过分类的.代码设计经验的总结. 使用设计模式的目的:为了代码可重用性.让代码更容易被他人理解.保证代码可靠性. 设计模式 ...

  2. SpringBoot系列: 单元测试2

    之前发了SpringBoot 单元测试的博客, https://www.cnblogs.com/harrychinese/p/springboot_unittesting.html , 内容较少, 现 ...

  3. BIO 和 NIO

    一.阻塞(Block)和非阻塞(NonBlock) 阻塞和非阻塞是进程在访问数据的时候,数据是否准备就绪的一种处理方式,当数据没有准备的时候阻塞: 阻塞:往往需要等待缞冲区中的数据准备好过后才处理其他 ...

  4. Linux 虚拟机上安装linux系统 (ip:子网掩码,网关,dns,交换机,路由知识回顾)

    一 安装虚拟机 二 虚拟机上配置好在安装linux系统 三 知识回顾 交换机:主机在局域网内的身份是MAC地址(可以通过[交换机广播:交换机通过被动学习来建立一张“接口号”和“MAC地址”的对照表]或 ...

  5. java中的stream的泛型方法的使用示例

    本文章使用jdk8测试 ,并结合使用lambda测试 测试前准备一些测试数据: class ObjectDemo { private Integer id; private String name; ...

  6. Ubuntu18.04更换国内源(阿里,网易,中科大,清华等源)

    1.备份 备份/etc/apt/sources.list文件 mv /etc/apt/sources.list /etc/apt/sourses.list.backup 2.新建 新建/etc/apt ...

  7. (原创)cocos lua 热更新从零开始(一)最简单demo

    开发环境:WIN7 + cocos2dx 3.10 lua版本 0.学习这篇内容的基础是你要会创建并运行一个cocos lua项目 1.热更新的思想所谓的热更新,就是在线更新代码和资源.热更新的过程首 ...

  8. java 根据Url下载对应的文件到指定位置,读txt文件获取url

    package test; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; im ...

  9. redis-hash

    Hash操作,redis中Hash在内存中的存储格式如下图: hset(name, key, value) # name对应的hash中设置一个键值对(不存在,则创建:否则,修改) # 参数: # n ...

  10. 使用mysqlslap进行MySQL压力测试

    使用mysqlslap进行MySQL压力测试发表于236 天前 ? MySQL ? 暂无评论 MySQL从5.1.4版开始带有一个压力测试工具mysqlslap,通过模拟多个并发客户端访问mysql来 ...