二分思想与相关问题(下)

接下来详细讲解几道比较难的例题,仔细体会二分和其他概念混合在一起的趣味。

下面这道题涉及了“碎片拼接”的概念,很妙,也很难想。

P r o b l e m 5 Problem5 Problem5 同时运行N台电脑的最长时间 LeetCode2141

你有 n 台电脑。给你整数 n 和一个下标从 0 开始的整数数组 batteries ,其中第 i 个电池可以让一台电脑 运行 batteries[i] 分钟。你想使用这些电池让 全部 n 台电脑 同时 运行。

一开始,你可以给每台电脑连接 至多一个电池 。然后在任意整数时刻,你都可以将一台电脑与它的电池断开连接,并连接另一个电池,你可以进行这个操作 任意次 。新连接的电池可以是一个全新的电池,也可以是别的电脑用过的电池。断开连接和连接新的电池不会花费任何时间。

注意,你不能给电池充电。

请你返回你可以让 n 台电脑同时运行的 最长 分钟数。

示例 1:

输入:n = 2, batteries = [3,3,3]
输出:4
解释:
一开始,将第一台电脑与电池 0 连接,第二台电脑与电池 1 连接。
2 分钟后,将第二台电脑与电池 1 断开连接,并连接电池 2 。注意,电池 0 还可以供电 1 分钟。
在第 3 分钟结尾,你需要将第一台电脑与电池 0 断开连接,然后连接电池 1 。
在第 4 分钟结尾,电池 1 也被耗尽,第一台电脑无法继续运行。
我们最多能同时让两台电脑同时运行 4 分钟,所以我们返回 4 。

示例 2:

输入:n = 2, batteries = [1,1,1,1]
输出:2
解释:
一开始,将第一台电脑与电池 0 连接,第二台电脑与电池 2 连接。
一分钟后,电池 0 和电池 2 同时耗尽,所以你需要将它们断开连接,并将电池 1 和第一台电脑连接,电池 3 和第二台电脑连接。
1 分钟后,电池 1 和电池 3 也耗尽了,所以两台电脑都无法继续运行。
我们最多能让两台电脑同时运行 2 分钟,所以我们返回 2 。

提示:

  • 1 <= n <= batteries.length <= 10^5
  • 1 <= batteries[i] <= 10^9

问题分析:

假设 N N N台电脑同时运行的时间的 t i m e time time,我们贪心地想,这个 t i m e time time越大,说明每台机器的耗电量越大,而电池的个数以及各自电量就那么点,就更容易把这些电池消耗完。也就是说,假设 N N N台电脑能够同时运行 t i m e time time时间,那么 N N N台电脑也一定能同时运行 t i m e − 1 time-1 time1时间,反之则不一定同时运行 t i m e + 1 time+1 time+1时间。

所以我们可以得出 t i m e time time N N N台电脑能否同时运行 t i m e time time时间具体单调函数关系:

现在我们来讨论怎么解决

​ 【问题5-1】给定整数 t i m e time time,返回 N N N台电脑能否同时运行 t i m e time time时间。

解决这个问题我们就要对电池的电量数组进行分析,如果某个电池的电量 e n e r g y > = t i m e energy >= time energy>=time,说明这个电池可以从0时刻开始就给某台电脑供电,直到 t i m e time time结束。我们称这种电池为”完美电池“,与之相对应的自然是碎片电池,碎片电池的电量 e n e r g y < t i m e energy < time energy<time。在整个分析过程中,我们可以把”完美电池单拎出来“,因为一个”完美电池“就可以完成一台电脑的供电,假设完美电池的个数为 m m m,那我们就只需要分析,在碎片电池组成的碎片电量数组条件下, N − m N-m Nm台电脑能否同时运行 t i m e time time时间。

这就涉及到**”碎片拼接“**问题了,我先给出结论:

​ 【结论5-1】只要碎片数组的累加和 s u m > = t i m e ∗ ( N − m ) sum >= time*(N-m) sum>=time(Nm),那么这N-m台电脑就可以同时运行time时间。

我们拿电量数组 [ 7 , 6 , 5 , 7 , 8 , 9 ] [7,6,5,7,8,9] [7,6,5,7,8,9](对应着 A , B , C , D , E , F A,B,C,D,E,F A,B,C,D,E,F号电池)来给甲乙丙丁四台电脑供电同时运行 10 10 10分钟来举例子。

从甲开始,甲先用 A A A电池供 7 7 7分钟电,剩下三分钟由 B B B电池供,那我就用 B B B电池给乙供电,供3分钟以便留3分钟给甲,那乙还需要七分钟,我们用 C C C电池给乙供剩下来的七分钟里的5分钟,剩余2分钟换 D D D供,那丙我就用 D D D供五分钟,再用 E E E供五分钟,丁先用 E E E供一分钟,剩下九分钟全用 F F F供。文字描述有点冗杂,同学们看下面这张图吧。

在这里插入图片描述

“碎片拼接”的证明我稍后再讲,现在我们只需要再分析一下二分筛选条件,这道题目就可以解出来了。

t i m e time time非负,所以左边界 l e f t = 0 left = 0 left=0 N N N台电脑的最长运行时间数肯定小于各电池的电量和 s u m sum sum,所以右边界 l e f t = s u m left = sum left=sum。如果这 N N N台机器不能同时运行 m i d mid mid时间,则选取左半部分作为收敛区间 r i g h t = m i d − 1 right = mid -1 right=mid1,如果这 N N N台机器可以同时运行 m i d mid mid时间,则选取右半部分作为收敛区间,并用 a n s w e r answer answer记录当前可行解 a n s w e r = m i d , l e f t = m i d + 1 answer = mid,left = mid+1 answer=mid,left=mid+1

解决代码:
bool check(vector<int>& batteries, long time, int n) {

    long sum = 0;
    for (int i : batteries) {
        if (i >= time) { // 完美電池
            n--;
            if (n == 0)
                return true;
        } else {
            sum += i;
        }
        if (sum >= time * n)
            return true;
    }
    return false;
}

long long maxRunTime(int n, vector<int>& batteries) {
    long left = 0;
    long All = 0;
    for (int i : batteries) {
        All += i;
    }
    long right = All;

    long long answer = 0;
    while (left <= right) {
        long mid = left + ((right - left) >> 1);
        if (check(batteries, mid, n)) {
            answer = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return answer;
}

这个解法其实还可以利用贪心分析进行优化,同学们可以自己思考思考。

P r o b l e m 6 Problem6 Problem6 计算等位时间

描述:

给定一个数组 a r r arr arr长度为 n n n,表示 n n n个服务员,每服务一个顾客的时间。

给定一个正数 m m m,表示有 m m m个人等位,如果你是刚来的人,每个客人遵循有空位就上的原则,请问你需要等多久?假设 m m m远远大于 n n n,比如 n < = 1 0 3 n <=10^3 n<=103, m < = 1 0 9 m <= 10^9 m<=109,该怎么做是最优解?【谷歌一连考了好几个月,很经典的情景题】

问题分析:

我们记等待时间为 t i m e time time,如果 t i m e time time和某个变量具有函数单调性,那么就可以对 t i m e time time进行二分。经历前五道题的磨炼我们知道,“某个变量”一定要和题目中给的条件扯上关系。

根据题目条件,服务员的个数以及每个服务员服务客户的时间是固定的,也就是说,这家餐厅在单位时间内的客户接待量是固定的【我们假设服务场景发生的餐厅,这个假设无关紧要,只是方便大家去理解】,排在我前面的顾客数量 m m m越大,那我们就需要等待更多的时间,换而言之,

​ 如果排在我前面的顾客数量为 m m m时,我们需要等待 t i m e 1 time_1 time1时间,那当排在我前面的顾客数量为 n , n < m n,n<m n,n<m时,我们需要等待的时间 t i m e 2 time_2 time2一定不会大于 t i m e 1 time_1 time1

根据上面分析我们很容易想到二分筛选条件:如果 t i m e time time时间内餐厅接待的客人个数 > = m >=m >=m,说明等待时间可以提前结束,我们选取左半部分区间来进行区间收敛 a n s w e r = m i d , r i g h t = m i d − 1 answer = mid,right = mid-1 answer=mid,right=mid1。如果 t i m e time time时间内餐厅接待的客人个数 < m <m <m,则说明在 t i m e time time时间过后仍有人在排队,我需要继续等待,所以我们选择右半部分区间来进行区间收敛 l e f t = m i d + 1 left = mid +1 left=mid+1

现在我们只需要解决下面的问题就能进行二分搜索,

​ 【问题6-1】给定时间 t i m e time time,请问餐厅在 t i m e time time时间内最多能接待多少个客人【接待指的是服务客人并结束服务, t i m e time time时刻还在接受服务的客人不参与计数】。

【问题6-1】利用贪心思想便可解决,要想服务的客人数量多,那服务员的工时就必须更长,我们就把服务员的工时拉满,全部工作 t i m e time time时间。最后只需要统计每个服务员工作 t i m e time time时间接待的顾客数,最后再求和即可。

在这里我给出【问题6-1】的解决代码:

bool check_Pro6(vector<int> arr, int time,int m) {
    int sum = 0;
    for (int i : arr) {
        sum += time / i;
    }
    if (sum >= m)    return true;
    else    return false;
}

二分筛选条件也讨论过,现在只差边界了,等待时间非负,所以左边界 l e f t = 0 left = 0 left=0,右边界可以这么去粗略确定,如果所有服务员的工作效率都和最慢的服务员一样,那么我需要等待的时间会比实际等待时间长很多,实际等待时间一定会小于 最低工作效率接待m个顾客所需要的时间。最低工作效率所需要的时间 对应着 a r r arr arr数组的最大值 m a x max max* ( m / n ) (m/n) (m/n)

解决代码:

在这里只给出主体框架二分代码:

int solution_Pro6(vector<int> arr, int m) {
    int max = 0;
    for (int i : arr) {
        max = i > max ? i : max;
    }

    int left = 0;
    int right = max * (m / arr.size());
    int answer;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (check_Pro6(arr, mid, m)) {
            answer = mid;
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return answer;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东秦小熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值