其他分享
首页 > 其他分享> > Maximum Number of Robots Within Budget

Maximum Number of Robots Within Budget

作者:互联网

Maximum Number of Robots Within Budget

You have $n$ robots. You are given two 0-indexed integer arrays, chargeTimes and runningCosts , both of length $n$. The i^{th} robot costs chargeTimes[i] units to charge and costs runningCosts[i] units to run. You are also given an integer budget .

The total cost of running $k$ chosen robots is equal to max(chargeTimes) + k * sum(runningCosts) , where max(chargeTimes) is the largest charge cost among the $k$ robots and sum(runningCosts) is the sum of running costs among the $k$ robots.

Return the maximum number of consecutive robots you can run such that the total cost does not exceed budget .

Example 1:

Input: chargeTimes = [3,6,1,3,4], runningCosts = [2,1,3,4,5], budget = 25
Output: 3
Explanation: 
It is possible to run all individual and consecutive pairs of robots within budget.
To obtain answer 3, consider the first 3 robots. The total cost will be max(3,6,1) + 3 * sum(2,1,3) = 6 + 3 * 6 = 24 which is less than 25.
It can be shown that it is not possible to run more than 3 consecutive robots within budget, so we return 3.

Example 2:

Input: chargeTimes = [11,12,19], runningCosts = [10,8,7], budget = 19
Output: 0
Explanation: No robot can be run that does not exceed the budget, so we return 0.

Constraints:

$chargeTimes.length == runningCosts.length == n$
$1 \leq n \leq 5 \times {10}^{4}$
$1 \leq chargeTimes[i], runningCosts[i] \leq {10}^{5}$
$1 \leq budget \leq {10}^{15}$

 

解题思路

  可以发现答案具有二段性,因此可以二分答案。假设最优解为长度为$ans$的区间,把这个区间右端点的元素去掉,可以发现区间最大值不会变大,而区间和变小(每个元素都是正整数),因此总代价减小。即如果最优解为$ans$,那么长度小于$ans$的区间也一定满足条件。因此具有二段性。

  然后在二分确定了区间长度后,每个区间的大小都是固定的,check函数就需要遍历所有长度相同的区间,并求每个区间的最大值和区间和,区间最值可以用单调队列来维护,区间和可以用一个变量来维护。

  AC代码如下,时间复杂度为$O(nlogn)$:

 1 class Solution {
 2 public:
 3     int n;
 4     long long m;
 5 
 6     bool check(int len, vector<int>& chargeTimes, vector<int>& runningCosts) {
 7         deque<int> q;
 8         long long s = 0;
 9         for (int i = 0; i < n; i++) {
10             // 维护区间最值
11             if (!q.empty() && i - q.front() + 1 > len) q.pop_front();
12             while (!q.empty() && chargeTimes[q.back()] <= chargeTimes[i]) {
13                 q.pop_back();
14             }
15             q.push_back(i);
16 
17             // 维护区间和
18             s += runningCosts[i];
19             if (i - len >= 0) s -= runningCosts[i - len];
20             
21             if (i - len + 1 >= 0 && chargeTimes[q.front()] + len * s <= m) return true;
22         }
23 
24         return false;
25     }
26 
27     int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
28         n = chargeTimes.size();
29         m = budget;
30         
31         int l = 0, r = n;
32         while (l < r) {
33             int mid = l + r + 1 >> 1;
34             if (check(mid, chargeTimes, runningCosts)) l = mid;
35             else r = mid - 1;
36         }
37 
38         return l;
39     }
40 };

  还可以用双指针。定义指针$j$为当指针$i$固定后,满足条件的最左边的位置。当$i$往右边移动,$j$也应该往右边移动。假设$i$往右移动为$i'$,对应的最左边的位置为$j'$,且$j'$在$j$的左边,由于$j' \sim i'$这个区间满足要求,而$j' \sim i$也满足要求(前面二段性的证明),因此就与$j$为指针$i$最左边的位置矛盾了。

  AC代码如下,时间复杂度为$O(n)$:

 1 class Solution {
 2 public:
 3     int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
 4         int n = chargeTimes.size();
 5         long long m = budget;
 6         
 7         deque<int> q;
 8         long long s = 0;
 9         int ret = 0;
10         for (int i = 0, j = 0; i < n; i++) {
11             s += runningCosts[i];
12             while (!q.empty() && chargeTimes[q.back()] <= chargeTimes[i]) {
13                 q.pop_back();
14             }
15             q.push_back(i);
16 
17             while (!q.empty() && chargeTimes[q.front()] + (i - j + 1) * s > m) {
18                 s -= runningCosts[j];
19                 if (q.front() == j) q.pop_front();
20                 j++;
21             }
22 
23             ret = max(ret, i - j + 1);
24         }
25 
26         return ret;
27     }
28 };

  最后扩展一下,题目要求的子序列是连续的,如果子序列可以不连续那应该怎么做呢?

  也是二分答案。先对所有机器人按照取最大值的那项 chargeTimes 按照从小到大的顺序排序,根据二分的值选出对应个数的机器人,假设是$len$个,枚举最大值选哪个,假设当前枚举到第$i$个,然后剩下的$len-1$个机器人一定是从排序后的第$i$个位置之前的机器人中选(如果从第$i$个位置后面选的话,那么最大值就不是第$i$个机器人了)。为了满足条件,应该从前面的机器人中选择$len-1$个最小的 runningCosts (总和应该尽可能小),这个可以用堆来维护。每次往堆插入一个元素,如果发现堆中的元素个数大于$k$,那么就把堆中最大的元素弹出。

  代码如下,时间复杂度为$O(n \cdot {log}^{2} n)$

 1 class Solution {
 2 public:
 3     int n;
 4     long long m;
 5     vector<int> idx;
 6 
 7     bool check(int len, vector<int>& chargeTimes, vector<int>& runningCosts) {
 8         priority_queue<int> pq;
 9         long long s = 0;
10         for (int i = 0; i < n; i++) {
11             if (pq.size() == len) {
12                 s -= pq.top();
13                 pq.pop();
14             }
15             pq.push(runningCosts[idx[i]]);
16             s += runningCosts[idx[i]];
17             if (pq.size() == len && chargeTimes[idx[i]] + len * s <= m) return true;
18         }
19 
20         return false;
21     }
22 
23     int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
24         n = chargeTimes.size();
25         m = budget;
26 
27         for (int i = 0; i < n; i++) {
28             idx.push_back(i);
29         }
30         sort(idx.begin(), idx.end(), [&](int a, int b) {
31             return chargeTimes[a] < chargeTimes[b];
32         });
33 
34         int l = 0, r = n;
35         while (l < r) {
36             int mid = l + r + 1 >> 1;
37             if (check(mid, chargeTimes, runningCosts)) l = mid;
38             else r = mid - 1;
39         }
40 
41         return l;
42     }
43 };

 

参考资料

  力扣第86场双周赛:https://www.bilibili.com/video/BV11G41157DN

标签:chargeTimes,int,runningCosts,Within,Robots,long,Budget,len,budget
来源: https://www.cnblogs.com/onlyblues/p/16656219.html