Leetcode 2528. 最大化城市的最小电量
注意到答案具有单调性,即值越大,越能/不能满足要求;值越小,越不能/能满足要求。所以二分答案。
check答案的逻辑为 判断用k次建站机会能否达到指定的最小电力值,这里考虑贪心的去建站,即从左向右遍历站点,如果当前站点 i 需要新的电力以达到目标电力值,则在 i+r的地方建x个电力站,x为目标电力值-当前站点电力值。建站后区间[i,i+2*r] 内所有的站点都增加了x点电力,这是个区间修改操作,于是考虑用差分数组(支持O(1)区间修改)。注意及时还原当前站点的差分数组,以获取当前站点电力值。(不用担心还原了差分数组而导致区间修改失效,因为还原的部分是在当前站点的左侧部分,而需要区间修改的部分总是在右侧,不影响,所以可以做到一边还原一边差分的“神奇”操作)。
class Solution {
public:
long long maxPower(vector<int>& stations, int r, int k) {
vector<long long>arr(stations.size() + 2, 0);
for(int i=0;i<stations.size();i++){
int left=max(0,i-r);
int right=min(i+r,static_cast<int>(stations.size()));
arr[left]+=stations[i];
arr[right + 1]-=stations[i];
}
long long left=0,right= (long long)(k)+100000ll*stations.size();
long long ans = 0;
while(left<=right){
long long mid=(left+right)/2; // 二分答案 ,假设mid是最终答案,即最小电力的最大值
if(k>=calNeed(arr,stations.size(),r,mid)){
// k是题目允许的最大建站数量,如果够用,还可以继续放大二分的答案,即收缩二分的左端点。
left=mid+1;
ans=mid;
}else{
right=mid-1;
}
}
return ans;
}
// 计算在指定最小电力值为now时,所需的建站数量
long long calNeed(vector<long long> arr,int stationSize,int r,long long now) {
long long need = 0;
for(int i=0;i<stationSize;i++) {
if(i>0)arr[i]+=arr[i-1]; // 还原差分数组
if(arr[i]<now) {
// 贪心:如果当前位置缺电力,则选择在 i+r的位置建站是最“经济实惠的”
long long count =now-arr[i]; // 建站的数量
// 差分数组的区间修改操作,整个区间都加上count
arr[i]+= count;
arr[min(i+2*r+1,stationSize)]-=count;
need+=count;
}
}
return need;
}
};