难度:★★★☆☆
类型:数组
方法:动态规划
力扣链接请移步本题传送门
更多力扣中等题的解决方案请移步力扣中等题目录
题目
我们将给定的数组 A 分成 K 个相邻的非空子数组 ,我们的分数由每个子数组内的平均值的总和构成。计算我们所能得到的最大分数是多少。
注意我们必须使用 A 数组中的每一个数进行分组,并且分数不一定需要是整数。
示例:
输入:
A = [9,1,2,3,9]
K = 3
输出: 20
解释:
A 的最优分组是[9], [1, 2, 3], [9]. 得到的分数是 9 + (1 + 2 + 3) / 3 + 9 = 20.
我们也可以把 A 分成[9, 1], [2], [3, 9].
这样的分组得到的分数为 5 + 2 + 6 = 13, 但不是最大值.
说明:
1 <= A.length <= 100.
1 <= A[i] <= 10000.
1 <= K <= A.length.
答案误差在 10^-6 内被视为是正确的。
解答
数组问题常用动态规划来解决。动态规划的精髓在于,把一个难以解决的大问题转化为若干个可以通过有限次重复解决的简单问题。接下来从动态规划的几个要素进行介绍:
【数组定义】
我们定义一个二维数组dp,维度为输入数组A的维度×K,dp[i][k]表示数组A[:i+1]被分成k+1组时,可以得到的最大分数(各组平均值的和)。这里尤其需要注意python下标和它代表的变量的物理含义之间正好相差1的。
【初始状态】
我们可以很快得知的信息是,如果数组只被分为一组的情况。因此可以先把k=0的一列填好。这一列各个位置dp[i][0]的填充规则,就是A[:i]的均值。
【递推公式】
我们以分组数k和下标i递增的方式,构造嵌套循环。这里还需要留意一个情况,就是填充dp[i][k]时,需要考虑到获得当前位置的各种情况的分组,因此借助一个中间下标变量j,范围从0到i,也就是说,我们把数组A[:i+1]从下标j砍成了两半,因此当前位置处的值可以填充为:
dp[i][k] = max(dp[i][k], dp[j][k-1]+average(j+1, i+1))
这里average是一个函数,用来计算A[i:j]子数组的均值。这里把i和j+1的原因是我们定义dp数组的物理意义决定的。
有了这个递推公式,就可以迭代进行计算了,
注意k的范围是从1到K,因为k=0的一列已经算好了;
i的范围是从k到len(A),因为组的个数不能比数组中数字的个数还要多;
j的范围是小于i,因为必须要保证可以把A[:i+1]分开。
【最后结果】
最终范围dp[-1][-1]也就是最后计算出的值,就可以代表题目所要求的结果。
class Solution(object):
def largestSumOfAverages(self, A, K):
P = [0]
for x in A:
P.append(P[-1] + x)
print(P)
def average(i, j):
return (P[j] - P[i]) / float(j - i)
N = len(A)
dp = [[0 for _ in range(K)] for _ in range(len(A))]
for i in range(len(A)):
dp[i][0] = average(0, i+1)
for k in range(1, K):
for i in range(k, N):
for j in range(i):
dp[i][k] = max(dp[i][k], dp[j][k-1]+average(j+1, i+1))
print("数组[{}]被分成{}份,平均值的和为{}".format(",".join(map(str, A[:i])), k+1, dp[i][k]))
print(dp)
return dp[-1][-1]
如有疑问或建议,欢迎评论区留言~
有关更多力扣中等题的python解决方案,请移步力扣中等题解析