LeetCode 力扣 2141 同时运行N台电脑的最长时间 二分答案

文章讲述了LeetCode的一道题目,涉及n台电脑和m个电池,目标是最大化电脑同时工作的最长时间。文章分析了简单的贪心策略,即每次选取电量最多的电池供电,但这种方法可能因时间复杂度过高而导致无法在规定时间内完成。然后提出了二分查找解决方案,通过检查在特定时间t内能否满足供电需求来找到最大运行时间。关键在于设计正确的check函数,并证明了条件的正确性。
摘要由CSDN通过智能技术生成

题目链接:LeetCode 2141 同时运行N台电脑的最长时间
题目大意:

一共有 n n n台电脑, m m m个电池,第 i i i个电池的电量为 b a t t e r i e s [ i ] batteries[i] batteries[i],每台电脑每一个时间段需要一个电池消耗一单位电量进行供电,在任意时刻你可以选择任意电量大于 0 0 0的电池给任意一台电脑供电,该过程不消耗时间,问最多能让 n n n台电脑同时工作多久。

数据范围:

1 ≤ n ≤ b a t t e r i e s . l e n g t h ≤ 1 0 5 1 \le n \le batteries.length \le 10^5 1nbatteries.length105
1 ≤ b a t t e r i e s [ i ] ≤ 1 0 9 1 \le batteries[i] \le 10^9 1batteries[i]109

题解:

一个简单的贪心就是每一时刻开始的时候选择电量剩余最多的 m m m个电池进行该时刻的供电,在下一时刻继续选择当前电量最多的继续供电,这样的想法是合理的,因为这样会尽可能的延迟出现某个电池的电量达到 0 0 0的时刻,而我们知道的是:只有有至少 n n n个电池的电量大于 0 0 0,那么就可以继续工作。所以只要保证出现电量为 0 0 0的时刻尽可能晚,那么使用时间就会尽可能的长,所以这个贪心是正确的。但是如果实现这个贪心策略出现的问题,因为每一次会选择 n n n个电池进行电量自减的操作,且该操作最多执行 ∑ i = 1 m b a t t e r i e s [ i ] n \frac {\sum_{i=1}^{m} batteries[i]}{n} ni=1mbatteries[i],即使我们能够 O ( 1 ) O(1) O(1)的做出自减操作也无法让程序在规定时间内完成(指 1 s 1s 1s执行 1 e 8 1e8 1e8条指令的前提下)。
那么为何二分答案可以解决呢?首先我们发现如果能够同时工作 t 0 t_0 t0时刻,那么一定可以同时工作 t < t 0 t<t_0 t<t0时刻,如果不能工作 t 0 t_0 t0时刻那么也不能工作 t > t 0 t>t_0 t>t0时刻,也就是答案满足单调性,这是能够进行二分答案的前提。
二分答案的关键在于 c h e c k check check函数,接下来我们考虑如何判断对于时间 t 0 t_0 t0,是否可以成功工作 t 0 t_0 t0时长。我们首先可以将电池电量进行排序,对于电池 i i i如果满足 b a t t e r i e s [ i ] ≥ t 0 batteries[i]\ge t_0 batteries[i]t0我们可以直接让该电池给一个电脑进行供电,因为这样的情况下,该电池是够用的,不会产生浪费(一个电池的电量最多使用 t 0 t_0 t0,不可能比这还大),解决完所有 b a t t e r i e s [ i ] ≥ t 0 batteries[i]\ge t_0 batteries[i]t0,如果此时所有的电脑都有一个 b a t t e r i e s [ i ] ≥ t 0 batteries[i]\ge t_0 batteries[i]t0的电池进行供电,那么显然此时可以撑过 t 0 t_0 t0的时间。所以我们接下来考虑现在还有 x x x个电脑没有被供电,还有 m ′ m' m块电池的情况(注意这些电池的电量均小于 t 0 t_0 t0)起始只要满足 x × t 0 ≤ ∑ i = 1 m ′ b a t t e r i e s [ i ] x\times t_0 \le \sum_{i=1}^{m'}batteries[i] x×t0i=1mbatteries[i]就能进行供电,下面进行证明:
首先,若满足上述条件,容易发现, m ′ > n m'>n m>n一定成立,接下来给出一定可以充分利用电池的方法:对于每一台电脑的供电一定至少需要两块电池进行供电,如果使用的电池对某一台电脑进行供电后没有任何剩余那么该电池一定没有被浪费,接下来考虑若某块电池给一台电脑供电后,还剩余电量的情况,由于剩余电量,那么该电池一定是最后一块给当前电脑供电的电池,我们将前面给该电脑供电的总时长记为 x 0 x_0 x0,该电池的电量记为 x 1 x_1 x1那么该电池的剩余电量为: x 0 + x 1 − t 0 x_0+x_1-t_0 x0+x1t0我们发现剩余电量一定是小于 x 0 x_0 x0的(若 x 0 + x 1 − t 0 ≥ x 0 x_0+x_1-t_0\ge x_0 x0+x1t0x0则可以推出 x 1 ≥ t 0 x_1\ge t_0 x1t0出现矛盾),这也就意味着,在前面的 x 0 x_0 x0时刻,我们可以将该电池给另一台电脑供电 x 0 + x 1 − t 0 x_0+x_1-t_0 x0+x1t0时刻, x 0 x_0 x0之后再将该电池给当前电脑进行供电,此时该电池的电量就没有发生浪费,其余的电池也是同理,所有的电池的电量都能够充分利用,因此此时只需要判断 x × t 0 ≤ ∑ i = 1 m ′ b a t t e r i e s [ i ] x\times t_0 \le \sum_{i=1}^{m'}batteries[i] x×t0i=1mbatteries[i]是否成立即可(即总电量大于等于需要消耗的电量)。

代码:

class Solution {
public:
    long long maxRunTime(int n, vector<int>& batteries) {
        long long l = 0, r = 0;
        for (auto battery : batteries) {
            r += battery;
        }
        r /= n;
        sort(batteries.begin(), batteries.end());
        while(l <= r) {
            long long mid = l + (r - l) / 2;
            if (check(mid, n, batteries)) { l = mid + 1; }
            else { r = mid - 1; }
        }
        return r;
    }
private:
    bool check(long long t0, int n, vector<int>& batteries) {
        int num = batteries.end() - lower_bound(batteries.begin(), batteries.end(), t0);
        long long sum = 0;
        for (int i = 0;i < batteries.size() - num; i++) { sum += batteries[i]; }
        return sum >= t0 * (n - num);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值