LC 2528. 最大化城市的最小供电站数目
https://leetcode.cn/problems/maximize-the-minimum-powered-city/
这个题解 https://leetcode.cn/problems/maximize-the-minimum-powered-city/solution/er-fen-da-an-qian-zhui-he-chai-fen-shu-z-jnyv/ 不错,后面说说这个解法。
这题外部框架就是二分法,来测试某个供电站的数量是否满足。然后安排供电站可以使用贪心算法:如果i位置不满足的话,那么我们可以在(i+r)这里安装供电站,这样在(i+r)这里安装的供电站其实可以贡献给 i,i+1,i+2,…i+r-1 这点位置。
一种方法是使用前缀和。比如计算ith的供电大小的话,可以使用 `acc[i+r] - acc[i-r-1]` 来计算。但是前缀和一个问题就是,如果我们更新了 `acc[i]` 的话还要更新后面的前缀和。不过因为我们这个是顺序检查的,其实可以把前缀和更新放在里面,比如下面这样。
def test(M, K): n = len(stations) acc = [0] * (n + 1) for i in range(min(n,r+1)): acc[i + 1] = acc[i] + stations[i] for i in range(n): begin = max(i - r, 0) end = min(i + r + 1, n) # end maybe has been updated. if end <= n: acc[end] = max(acc[end], acc[end - 1] + stations[end - 1]) a = acc[begin] b = acc[end] if b - a < M: extra = M + a - b if extra > K: return False K -= extra acc[end] += extra return True
另一种方法就是上面题解里面说的,我们可以使用差分数组。我其实没有太搞明白差分数组的概念,但是大概明白他的思路:如果我们在i位置上增加m的话,那么需要在 i+r+1 这个位置上取消这个m(因为覆盖范围就是到i+r). 这种差分数组的好处就是不用更新前缀和。这题如果每次计算前缀和,时间大约是9.5s(压线通过),但是如果使用差分数组的话,可以在6.8s左右。
class Solution: def maxPower(self, stations: List[int], r: int, k: int) -> int: n = len(stations) acc = [0] * (n + 1) for i in range(n): acc[i + 1] = acc[i] + stations[i] def test(M, K): diff = [0] * (n + 1) DIFF = 0 for i in range(n): begin = max(i - r, 0) end = min(i + r + 1, n) DIFF += diff[i] SUM = acc[end] - acc[begin] + DIFF if SUM < M: extra = M - SUM if extra > K: return False K -= extra DIFF += extra diff[min(i + r + 1 + r, n)] -= extra return True INF = k + sum(stations) s, e = 0, INF while s <= e: m = (s + e) // 2 if test(m, k): s = m + 1 else: e = m - 1 ans = e return ans