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?html
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
.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的本质是什么,不就是状态转移方程,根据前面的状态来更新当前的状态。而子数组不就是整个数组的前一个状态,不停的更新的使得咱们最终能获得极值。spa
好,下面进入正题。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],因此咱们能够得出状态转移方程以下:code
dp[i][k] = max(avg(i, n) + max_{j > i} (avg(i, j) + dp[j][k-1]))orm
这里的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)中的每一个位置了,好像也没什么不妥的地方,有兴趣的童鞋能够尝试的写一下~htm
解法一:blog
class Solution { public: double largestSumOfAverages(vector<int>& A, int K) { int n = A.size(); vector<double> sums(n + 1); vector<vector<double>> dp(n, vector<double>(K)); for (int i = 0; i < n; ++i) { sums[i + 1] = sums[i] + A[i]; } for (int i = 0; i < n; ++i) { dp[i][0] = (sums[n] - sums[i]) / (n - i); } for (int k = 1; k < K; ++k) { for (int i = 0; i < n - 1; ++i) { for (int j = i + 1; j < n; ++j) { dp[i][k] = max(dp[i][k], (sums[j] - sums[i]) / (j - i) + dp[j][k - 1]); } } } return dp[0][K - 1]; } };
咱们能够对空间进行优化,因为每次的状态k,只跟前一个状态k-1有关,因此咱们不须要将全部的状态都保存起来,只须要保存前一个状态的值就好了,那么咱们就用一个一维数组就能够了,不断的进行覆盖,从而达到了节省空间的目的,参见代码以下:递归
解法二:ip
class Solution { public: double largestSumOfAverages(vector<int>& A, int K) { int n = A.size(); vector<double> sums(n + 1); vector<double> dp(n); for (int i = 0; i < n; ++i) { sums[i + 1] = sums[i] + A[i]; } for (int i = 0; i < n; ++i) { dp[i] = (sums[n] - sums[i]) / (n - i); } for (int k = 1; k < K; ++k) { for (int i = 0; i < n - 1; ++i) { for (int j = i + 1; j < n; ++j) { dp[i] = max(dp[i], (sums[j] - sums[i]) / (j - i) + dp[j]); } } } return dp[0]; } };
咱们也能够是用递归加记忆数组的方式来实现,记忆数组的运做原理和DP十分相似,也是一种cache,将已经计算过的结果保存起来,用的时候直接取便可,避免了大量的重复计算,参见代码以下:
解法三:
class Solution { public: double largestSumOfAverages(vector<int>& A, int K) { int n = A.size(); vector<vector<double>> memo(101, vector<double>(101)); double cur = 0; for (int i = 0; i < n; ++i) { cur += A[i]; memo[i + 1][1] = cur / (i + 1); } return helper(A, K, n, memo); } double helper(vector<int>& A, int k, int j, vector<vector<double>>& memo) { if (memo[j][k] > 0) return memo[j][k]; double cur = 0; for (int i = j - 1; i > 0; --i) { cur += A[i]; memo[j][k] = max(memo[j][k], helper(A, k - 1, 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/