二分 & 贪心
解法:二分 & 贪心
求最小值最大,容易想到二分答案。接下来思考如何确定二分值 $V$ 是否有效。
我们可以用一个滑动窗口从左到右计算每个城市的电量。若当前城市电量不足,我们需要新建供电站补上。由于左边的城市的电量都大于等于 $V$,因此新的供电站应该尽量建在右边。显然这些供电站应该建在滑动窗口内部的最右侧。
二分答案后模拟以上过程即可。复杂度 $\mathcal{O}(n\log (nA + k))$,其中 $A$ 是 stations[i] 的最大值。
参考代码(c++)
###c++
class Solution {
public:
long long maxPower(vector<int>& stations, int R, int K) {
int n = stations.size();
auto check = [&](long long LIM) {
vector<long long> vec;
for (int x : stations) vec.push_back(x);
// 初始化滑动窗口的和
long long sm = 0;
for (int i = 0; i <= R && i < n; i++) sm += vec[i];
// 表示还有几个供电站可以新建
long long rem = K;
// 从左到右计算每个城市的电量,同时维护滑动窗口 [l, r]
for (int i = 0, l = 0, r = R; ; i++) {
if (sm < LIM) {
// 当前城市电量不足
long long delta = LIM - sm;
// 新供电站不够,返回 false
if (delta > rem) return false;
// 新供电站足够,建在滑动窗口最右边
rem -= delta;
vec[r] += delta;
sm += delta;
}
if (i == n - 1) break;
// 滑动窗口向前移动一个城市
if (i >= R) sm -= vec[l], l++;
if (r != n - 1) sm += vec[r + 1], r++;
}
return true;
};
// 二分答案
long long head = 0, tail = 2e10;
while (head < tail) {
long long mid = (head + tail + 1) >> 1;
if (check(mid)) head = mid;
else tail = mid - 1;
}
return head;
}
};