给你一个下标从 0 开始长度为 n
的整数数组 stations
,其中 stations[i]
表示第 i
座城市的供电站数目。
每个供电站可以在一定 范围 内给所有城市提供电力。换句话说,如果给定的范围是 r
,在城市 i
处的供电站可以给所有满足 |i - j| <= r
且 0 <= i, j <= n - 1
的城市 j
供电。
|x|
表示x
的 绝对值 。比方说,|7 - 5| = 2
,|3 - 10| = 7
。
一座城市的 电量 是所有能给它供电的供电站数目。
政府批准了可以额外建造 k
座供电站,你需要决定这些供电站分别应该建在哪里,这些供电站与已经存在的供电站有相同的供电范围。
给你两个整数 r
和 k
,如果以最优策略建造额外的发电站,返回所有城市中,最小电量的最大值是多少。
这 k
座供电站可以建在多个城市。
示例 1:
输入:stations = [1,2,4,5,0], r = 1, k = 2 输出:5 解释: 最优方案之一是把 2 座供电站都建在城市 1 。 每座城市的供电站数目分别为 [1,4,4,5,0] 。 - 城市 0 的供电站数目为 1 + 4 = 5 。 - 城市 1 的供电站数目为 1 + 4 + 4 = 9 。 - 城市 2 的供电站数目为 4 + 4 + 5 = 13 。 - 城市 3 的供电站数目为 5 + 4 = 9 。 - 城市 4 的供电站数目为 5 + 0 = 5 。 供电站数目最少是 5 。 无法得到更优解,所以我们返回 5 。
示例 2:
输入:stations = [4,4,4,4], r = 0, k = 3 输出:4 解释: 无论如何安排,总有一座城市的供电站数目是 4 ,所以最优解是 4 。
提示:
n == stations.length
1 <= n <= 10^5
0 <= stations[i] <= 10^5
0 <= r <= n - 1
0 <= k <= 10^9
提示 1
Pre calculate the number of stations on each city using Line Sweep.
提示 2
Use binary search to maximize the minimum.
解法:二分 + 前缀和 + 差分 + 贪心
提示 1
看到「最大化最小值」或者「最小化最大值」就要想到二分答案,这是一个固定的套路。
为什么?一般来说,二分的值越大,越能/不能满足要求;二分的值越小,越不能/能满足要求,有单调性,可以二分。
提示 2
二分答案 minPower,从左到右遍历 stations,如果 stations[i] 电量不足 minPower,那么需要建供电站来补足。
在哪建供电站最好呢?
提示 3
从左到右遍历,由于 i 左侧的电量都已处理完毕,所以在 i 右侧建供电站,所以贪心地在 i + r
处建供电站,由于不能超出数组下标范围,所以在 min(i+r,n−1) 处建是最合适的,恰好让 i 在新建供电站的覆盖范围的左边界上。此时新的供电站的覆盖范围为 (i, i + 2r ),考虑数组下标范围,即为( i, Math.min(i + 2r, n - 1))。
提示 4
假设 i + r 处需要建 m 个供电站才能补足 i 处的电量到minPower,那么需要把 [i,min(i + 2r,n−1)] 范围内的电量都增加 m,用差分数组来更新是最简单的。
最后判断总的需要新建的供电站数量是否超过 k,如果超过说明答案 minPower 偏大,否则说明偏小。
如果以上提示没看明白,请先做以下题目:
关于对差分数组的详细讲解:LeetCode 1094. 拼车-CSDN博客
与本题同类型题目的详细讲解:LeetCode 2772. 使数组中的所有元素都等于零-CSDN博客
Java版:
class Solution {
public long maxPower(int[] stations, int r, int k) {
int n = stations.length;
long[] presum = new long[n + 1];
long min = Long.MAX_VALUE;
for (int i = 1; i <= n; i++) {
presum[i] = presum[i - 1] + stations[i - 1];
}
// power[]数组记录每个城市的电量
long[] power = new long[n];
for (int i = 0; i < n; i++) {
// [i - r, i + r] 范围内的供电站都给i处的城市供电
power[i] = presum[Math.min(i + r + 1, n)] - presum[Math.max(i - r, 0)];
min = Math.min(min, power[i]);
}
long left = min;
long right = min + k;
while (left <= right) {
long mid = left + (right - left) / 2;
if (check(mid, power, n, r, k)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
private boolean check(long minPower, long[] power, int n, int r, int k) {
int need = 0;
long sumD = 0;
long[] diff = new long[n];
for (int i = 0; i < n; i++) {
// 累加差分值
sumD += diff[i];
long m = minPower - power[i] - sumD;
if (m > 0) {
// 需要新建m个供电站
need += m;
if (need > k) {
return false;
}
// 差分值更新
sumD += m;
if (i + 2 * r + 1 < n) {
diff[i + 2 * r + 1] -= m;
}
}
}
return true;
}
}
Python3版:
标准数据类型
Python3 中常见的数据类型有:
- Number(数字)
- String(字符串)
- bool(布尔类型)
- List(列表)
- Tuple(元组)
- Set(集合)
- Dictionary(字典)
Python3 的六个标准数据类型中:
- 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
- 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
此外还有一些高级的数据类型,如: 字节数组类型(bytes)。
Number(数字)
Python3 支持 int、float、bool、complex(复数)。
在Python 3里,只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
class Solution:
def maxPower(self, stations: List[int], r: int, k: int) -> int:
n = len(stations)
presum = [0] * (n + 1)
for i in range(1, n + 1):
presum[i] = presum[i - 1] + stations[i - 1]
power = [0] * n
for i in range(n):
power[i] = presum[min(i + r + 1, n)] - presum[max(i - r, 0)]
left = min(power)
right = left + k
while left <= right:
mid = left + (right - left) // 2
if self.check(mid, power, n, r, k):
left = mid + 1
else:
right = mid - 1
return right
def check(self, minPower: int, power: List[int], n: int, r: int, k: int) -> bool:
diff = [0] * n
need = 0
sumD = 0
for i in range(n):
sumD += diff[i]
m = minPower - power[i] - sumD
if m > 0:
need += m
if need > k:
return False
sumD += m
if i + 2 * r + 1 < n:
diff[i + 2 * r + 1] -= m
return True
复杂度分析
- 时间复杂度:O(nlogk),其中 n 为 stations 的长度。二分需要循环 O(logk) 次。总的时间复杂度O(n + n logk) = O(nlogk)
- 空间复杂度:O(n)。