|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[813. 最大平均值和的分组](https://leetcode.cn/problems/largest-sum-of-averages/solution/by-ac_oier-yfnt/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「序列 DP」、「前缀和」、「动态规划」、「数学」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +给定数组 `nums` 和一个整数 `k` 。我们将给定的数组 `nums` 分成 最多 `k` 个相邻的非空子数组 。 分数 由每个子数组内的平均值的总和构成。 |
| 10 | + |
| 11 | +注意我们必须使用 `nums` 数组中的每一个数进行分组,并且分数不一定需要是整数。 |
| 12 | + |
| 13 | +返回我们所能得到的最大 分数 是多少。答案误差在 10ドル^{-6}$ 内被视为是正确的。 |
| 14 | + |
| 15 | +示例 1: |
| 16 | +``` |
| 17 | +输入: nums = [9,1,2,3,9], k = 3 |
| 18 | + |
| 19 | +输出: 20.00000 |
| 20 | + |
| 21 | +解释: |
| 22 | +nums 的最优分组是[9], [1, 2, 3], [9]. 得到的分数是 9 + (1 + 2 + 3) / 3 + 9 = 20. |
| 23 | +我们也可以把 nums 分成[9, 1], [2], [3, 9]. |
| 24 | +这样的分组得到的分数为 5 + 2 + 6 = 13, 但不是最大值. |
| 25 | +``` |
| 26 | +示例 2: |
| 27 | +``` |
| 28 | +输入: nums = [1,2,3,4,5,6,7], k = 4 |
| 29 | + |
| 30 | +输出: 20.50000 |
| 31 | +``` |
| 32 | + |
| 33 | +提示: |
| 34 | +* 1ドル <= nums.length <= 100$ |
| 35 | +* 1ドル <= nums[i] <= 10^4$ |
| 36 | + |
| 37 | +--- |
| 38 | + |
| 39 | +### 前缀和 + 序列 DP |
| 40 | + |
| 41 | +题意可整理为一句话:将 $n$ 个元素划分为「最多」$m$ 个连续段,最大化连续段的平均值之和。 |
| 42 | + |
| 43 | +为了方便,我们令所有数组下标从 1ドル$ 开始。 |
| 44 | + |
| 45 | +定义 $f[i][j]$ 为考虑将前 $i$ 个元素划分成 $j$ 份的最大平均和,答案为 $f[n][k],ドル其中 1ドル \leq k \leq m$。 |
| 46 | + |
| 47 | +不失一般性考虑 $f[i][j]$ 该如何计算,由于划分出来的子数组不能是空集,因此我们可以根据 $j$ 的大小分情况讨论: |
| 48 | + |
| 49 | +* 当 $j = 1,ドル此时有 $f[i][j] = \frac{\sum_{idx = 1}^{i} nums[idx - 1]}{i}$ |
| 50 | +* 当 $j > 1,ドル此时枚举最后一个子数组的起点 $k,ドル其中 2ドル \leq k \leq i,ドル此时有平均值之和为 $f[k - 1][j - 1] + \frac{\sum_{idx = k}^{i} nums[idx]}{i - k + 1},ドル最终 $f[i][j]$ 为枚举所有 $k$ 值的最大值 |
| 51 | + |
| 52 | +其中求解连续段之和可以用「前缀和」进行优化。同时,想要简化代码,还可以利用一个简单的数学结论:划分份数越多,平均值之和越大,因此想要取得最大值必然是恰好划分成 $m$ 份。 |
| 53 | + |
| 54 | +Java 代码: |
| 55 | +```Java |
| 56 | +class Solution { |
| 57 | + public double largestSumOfAverages(int[] nums, int m) { |
| 58 | + int n = nums.length; |
| 59 | + double[] sum = new double[n + 10]; |
| 60 | + for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1]; |
| 61 | + double[][] f = new double[n + 10][m + 10]; |
| 62 | + for (int i = 1; i <= n; i++) { |
| 63 | + for (int j = 1; j <= Math.min(i, m); j++) { |
| 64 | + if (j == 1) { |
| 65 | + f[i][1] = sum[i] / i; |
| 66 | + } else { |
| 67 | + for (int k = 2; k <= i; k++) { |
| 68 | + f[i][j] = Math.max(f[i][j], f[k - 1][j - 1] + (sum[i] - sum[k - 1]) / (i - k + 1)); |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + return f[n][m]; |
| 74 | + } |
| 75 | +} |
| 76 | +``` |
| 77 | +TypeScript 代码: |
| 78 | +```TypeScript |
| 79 | +function largestSumOfAverages(nums: number[], m: number): number { |
| 80 | + const n = nums.length |
| 81 | + const sum = new Array<number>(n + 10).fill(0) |
| 82 | + for (let i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1] |
| 83 | + const f = new Array<Array<number>>() |
| 84 | + for (let i = 0; i < n + 10; i++) f[i] = new Array<number>(m + 10).fill(0) |
| 85 | + for (let i = 1; i <= n; i++) { |
| 86 | + for (let j = 1; j <= Math.min(i, m); j++) { |
| 87 | + if (j == 1) { |
| 88 | + f[i][j] = sum[i] / i |
| 89 | + } else { |
| 90 | + for (let k = 2; k <= i; k++) { |
| 91 | + f[i][j] = Math.max(f[i][j], f[k - 1][j - 1] + (sum[i] - sum[k - 1]) / (i - k + 1)) |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | + return f[n][m] |
| 97 | +} |
| 98 | +``` |
| 99 | +Python 代码: |
| 100 | +```Python |
| 101 | +class Solution: |
| 102 | + def largestSumOfAverages(self, nums: List[int], m: int) -> float: |
| 103 | + n = len(nums) |
| 104 | + psum = [0] * (n + 10) |
| 105 | + for i in range(1, n + 1): |
| 106 | + psum[i] = psum[i - 1] + nums[i - 1] |
| 107 | + f = [[0] * (m + 10) for _ in range(n + 10)] |
| 108 | + for i in range(1, n + 1): |
| 109 | + for j in range(1, min(i, m) + 1): |
| 110 | + if j == 1: |
| 111 | + f[i][j] = psum[i] / i |
| 112 | + else: |
| 113 | + for k in range(2, i + 1): |
| 114 | + f[i][j] = max(f[i][j], f[k - 1][j - 1] + (psum[i] - psum[k - 1]) / (i - k + 1)) |
| 115 | + return f[n][m] |
| 116 | +``` |
| 117 | +* 时间复杂度:$O(n^2 \times m)$ |
| 118 | +* 空间复杂度:$O(n \times m)$ |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +### 最后 |
| 123 | + |
| 124 | +这是我们「刷穿 LeetCode」系列文章的第 `No.813` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 125 | + |
| 126 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 127 | + |
| 128 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 129 | + |
| 130 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 131 | + |
0 commit comments