Maximize the Minimum Powered City

Maximize the Minimum Powered City

You are given a 0-indexed integer array stations of length $n$, where stations[i] represents the number of power stations in the ith city.

Each power station can provide power to every city in a fixed range. In other words, if the range is denoted by $r$, then a power station at city $i$ can provide power to all cities $j$ such that $|i - j| \leq r$ and $0 \leq i, j \leq n - 1$.

  • Note that $|x|$ denotes absolute value. For example, $|7 - 5| = 2$ and $|3 - 10| = 7$.

The power of a city is the total number of power stations it is being provided power from.

The government has sanctioned building $k$ more power stations, each of which can be built in any city, and have the same range as the pre-existing ones.

Given the two integers $r$ and $k$, return the maximum possible minimum power of a city, if the additional power stations are built optimally.

Note that you can build the $k$ power stations in multiple cities.

Example 1:

Input: stations = [1,2,4,5,0], r = 1, k = 2
Output: 5
Explanation: 
One of the optimal ways is to install both the power stations at city 1. 
So stations will become [1,4,4,5,0].
- City 0 is provided by 1 + 4 = 5 power stations.
- City 1 is provided by 1 + 4 + 4 = 9 power stations.
- City 2 is provided by 4 + 4 + 5 = 13 power stations.
- City 3 is provided by 5 + 4 = 9 power stations.
- City 4 is provided by 5 + 0 = 5 power stations.
So the minimum power of a city is 5.
Since it is not possible to obtain a larger power, we return 5.

Example 2:

Input: stations = [4,4,4,4], r = 0, k = 3
Output: 4
Explanation: 
It can be proved that we cannot make the minimum power of a city greater than 4.

Constraints:

  • $n ~\mathrm{==}~ \text{stations.length}$
  • $1 \leq n \leq {10}^5$
  • $0 \leq \text{stations}[i] \leq {10}^5$
  • $0 \leq r \leq n - 1$
  • $0 \leq k \leq {10}^9$

 

解题思路

  看到最小供电站数目的最大值是多少,也就是问最小值的最大化是多少,就应该想想能不能用二分来做。如果答案是$\text{ans}$,那么很明显超过$\text{ans}$的值是不可能取到的,如果小于等于$\text{ans}$,因为可以保证存在每个城市的最小供电站数目至少为$\text{ans}$的方案,因此该方案对于小于等于$\text{ans}$的值也成立,因此满足二段性可以进行二分。

  先求出每个$\text{stations}[i]$包含的供电站数目$s[i]$(枚举每个下标然后对$[i-r, i+r]$范围内都加上$\text{stations}[i]$,表示第$i$个城市可以为这个范围内的城市贡献$\text{stations}[i]$,用差分实现)。二分出$\text{mid}$,$\text{check}$时从左到右遍历$s$数组,如果发现第$i$个城市的供电站数目不足$\text{mid}$,那么就要加供电站,因为已经保证前$i$个城市的供电站数目至少为$\text{mid}$,因此供电站可以在$[i, i + r]$范围内加。这时贪心地想,越靠右,那么新加的供电站能够对越多的城市有贡献,因此新加的供电站放置在$i+r$这个位置上,新加的供电站内对$[i, i + 2r]$范围内的城市有贡献。实现的话可以开个双端队列,每当需要加新的供电站时就往队列压入新加的供电站的能影响到的最远下标(即$i+2r$),然后每次循环都看看队头元素有没有落后$i$,落后了就弹出。在这个过程中还需要维护在有效范围内新加的供电站的总数目$sum$。

  AC代码如下:

 1 class Solution {
 2 public:
 3     long long maxPower(vector<int>& stations, int r, int k) {
 4         int n = stations.size();
 5         vector<long long> s(n + 1);
 6         for (int i = 0; i < n; i++) {
 7             s[max(0, i - r)] += stations[i];
 8             s[min(n - 1, i + r) + 1] -= stations[i];
 9         }
10         for (int i = 1; i < n; i++) {
11             s[i] += s[i - 1];
12         }
13         long long left = 0, right = accumulate(stations.begin(), stations.end(), 0ll) + k;
14         auto check = [&](long long mid) {
15             deque<pair<int, long long>> q;
16             long long sum = 0, cnt = k;
17             for (int i = 0; i < n; i++) {
18                 if (!q.empty() && q.front().first < i) {    // 队头的供电站能够影响的最远距离落后于i
19                     sum -= q.front().second;    // 减去这些供电站
20                     q.pop_front();  // 弹出队列
21                 }
22                 if (mid - s[i] - sum > cnt) return false;   // 剩下能提供的供电站无法满足需求
23                 if (mid - s[i] - sum > 0) {
24                     int t = mid - s[i] - sum;
25                     cnt -= t, sum += t;
26                     q.push_back({i + r + r, t});    // 供电站放置在最右处,即i+r,供电站能影响的最远范围是i+2r
27                 }
28             }
29             return true;
30         };
31         while (left < right) {
32             long long mid = left + right + 1 >> 1;
33             if (check(mid)) left = mid;
34             else right = mid - 1;
35         }
36         return left;
37     }
38 };

  也可以用差分来实现,本质是在$[i, i + 2r]$范围内的城市都加上$t$,遍历的过程中开个$sum$来求差分数组的前缀和。

  AC代码如下:

 1 class Solution {
 2 public:
 3     long long maxPower(vector<int>& stations, int r, int k) {
 4         int n = stations.size();
 5         vector<long long> s(n + 1);
 6         for (int i = 0; i < n; i++) {
 7             s[max(0, i - r)] += stations[i];
 8             s[min(n - 1, i + r) + 1] -= stations[i];
 9         }
10         for (int i = 1; i < n; i++) {
11             s[i] += s[i - 1];
12         }
13         long long left = 0, right = accumulate(stations.begin(), stations.end(), 0ll) + k;
14         auto check = [&](long long mid) {
15             vector<long long> d(n + 1); // 差分数组
16             long long sum = 0, cnt = k;
17             for (int i = 0; i < n; i++) {
18                 sum += d[i];    // 对差分数组求前缀和
19                 if (mid - s[i] - sum > cnt) return false;
20                 if (mid - s[i] - sum > 0) {
21                     int t = mid - s[i] - sum;
22                     cnt -= t, sum += t; // 由于d[i] += t,这里对这步省略,直接加在sum上(此时的值就是1~i的前缀和)
23                     d[min(n - 1, i + r + r) + 1] -= t;
24                 }
25             }
26             return true;
27         };
28         while (left < right) {
29             long long mid = left + right + 1 >> 1;
30             if (check(mid)) left = mid;
31             else right = mid - 1;
32         }
33         return left;
34     }
35 };

 

参考资料

  二分答案+前缀和+差分数组+贪心(Python/Java/C++/Go):https://leetcode.cn/problems/maximize-the-minimum-powered-city/solution/er-fen-da-an-qian-zhui-he-chai-fen-shu-z-jnyv/

posted @ 2023-01-13 16:05  onlyblues  阅读(17)  评论(0编辑  收藏  举报
Web Analytics