【秋招刷题打卡】Day03-二分系列之-二分答案

Day03-二分系列之-二分答案

给大家推荐一下咱们的 陪伴打卡小屋 知识星球啦,详细介绍 =>笔试刷题陪伴小屋-打卡赢价值丰厚奖励 <=

⏰小屋将在每日上午发放打卡题目,包括:

  • 一道该算法的模版题 (主要以力扣,牛客,acwing等其他OJ网站的题目作为模版)
  • 一道该算法的应用题(主要以往期互联网大厂 笔试真题 的形式出现,评测在咱们的 笔试突围OJ

在这里插入图片描述

小屋day03

咱们的二分系列用的都是 day02 的二分模版~

二分模版介绍:二分模版

有小伙伴可能会问了,既然系统有自带的二分库,为什么我们还要自己手写二分呢?清隆认为主要有以下两点

  • 在二分的其他应用中,手写二分更利于设置 check 条件和实现具体逻辑
  • 加深对二分的理解和提高代码熟练度

今天给大家带来二分答案相关的题

前言

什么是二分答案

说直白点其实就是先 一个答案,然后通过check函数来检查这个答案 是否合法,继而跟新二分的左右端点。

能够二分答案的题需要题目满足具有一定的性质,如单调性、二段性等等。

🎀 模版题

2187. 完成旅途的最少时间

题目描述

给你一个数组 time ,其中 time[i] 表示第 i 辆公交车完成 一趟 旅途 所需要花费的时间。

每辆公交车可以 连续 完成多趟旅途,也就是说,一辆公交车当前旅途完成后,可以 立马开始 下一趟旅途。每辆公交车 独立 运行,也就是说可以同时有多辆公交车在运行且互不影响。

给你一个整数 totalTrips ,表示所有公交车 总共 需要完成的旅途数目。请你返回完成 至少 totalTrips 趟旅途需要花费的 最少 时间。

示例 1:

输入:time = [1,2,3], totalTrips = 5
输出:3
解释:
- 时刻 t = 1 ,每辆公交车完成的旅途数分别为 [1,0,0] 。
  已完成的总旅途数为 1 + 0 + 0 = 1 。
- 时刻 t = 2 ,每辆公交车完成的旅途数分别为 [2,1,0] 。
  已完成的总旅途数为 2 + 1 + 0 = 3 。
- 时刻 t = 3 ,每辆公交车完成的旅途数分别为 [3,1,1] 。
  已完成的总旅途数为 3 + 1 + 1 = 5 。
所以总共完成至少 5 趟旅途的最少时间为 3 。

示例 2:

输入:time = [2], totalTrips = 1
输出:2
解释:
只有一辆公交车,它将在时刻 t = 2 完成第一趟旅途。
所以完成 1 趟旅途的最少时间为 2 。

解题思路

时间越多,可以完成的旅途也就越多,有 单调性,可以二分答案

在二分之前需要设置区间左右端。

  • 左端点:答案的最小可能值

  • 右端点:答案的最大可能值

  • 当然实际运用中,左端点可以设置的更小点也没事,右端点同理

现在我们尝试来进行 答案,假设当前答案为 X:

那么可以完成的旅途数量为: ∑ i = 0 n − 1 ⌊ x t i m e [ i ] ⌋ \sum_{i=0}^{n - 1} \lfloor \frac{x}{time[i]} \rfloor i=0n1time[i]x

如果比 totalTrips 大了,说明 X 还能继续缩小,那么跟新(左移)右端点,否则跟新(右移)左端点。

此时我们发现在 check 函数判断之后 需要跟新的是右端点,所以使用 第一个二分模版

时间复杂度:O(nlog⁡U),其中 n 为time 的长度,U 为 二分设置的区间长度

参考代码

  • Python

    class Solution:
        def minimumTime(self, time: List[int], totalTrips: int) -> int:
            l, r = 0, int(1e18) + 10 #  设置左右端点
            
            def check(x: int) -> bool:
                sum = 0
                for t in time:
                    sum += x // t
                    if sum >= totalTrips:
                        return True
                return False
            
            while l < r:
                mid = (l + r) // 2
                if check(mid):
                    r = mid
                else:
                    l = mid + 1
            
            return l
    
    
  • Java

    import java.util.List;
    
    class Solution {
        public long minimumTime(int[] time, int totalTrips) {
            long l = 0, r = (long)1e18 + 10; // 设置左右端点
            
            while (l < r) {
                long mid = l + (r - l) / 2;
                if (check(time, mid, totalTrips)) {
                    r = mid;
                } else {
                    l = mid + 1;
                }
            }
            return l;
        }
    
        private boolean check(int[] time, long x, int totalTrips) {
            long sum = 0;
            for (int t : time) {
                sum += x / t;
                if (sum >= totalTrips) {
                    return true;
                }
            }
            return false;
        }
    }
    
    
  • Cpp

    class Solution {
    public:
        long long minimumTime(vector<int>& time, int totalTrips) {
            long long l = 0, r = 1e18 + 10; // 设置左右端点
            auto check = [&](long long x) -> bool{
                long long sum = 0;
                for(int t : time)
                {
                    sum += x / t;
                    if(sum >= totalTrips) return true;
                }
                return false;
            };
            while(l < r)
            {
                long long mid = l + r >> 1; 
                if(check(mid)) r = mid;
                else
                    l = mid + 1;
            }
            return l;
        }
    };
    

🍰 笔试真题

  • 该题来自今年 华为春招 的笔试题,出现在笔试第一题。

K小姐的购物系统调度

评测链接🔗

问题描述

K小姐负责维护一个购物系统,该系统面临着来自多个客户端的请求。为了应对系统的性能瓶颈,需要实现一个降级策略,以防止系统超负荷。

系统需要确定一个请求调用量的最大阈值 v a l u e value value。如果所有客户端的请求总量未超过系统的最大承载量 c n t cnt cnt,则所有请求均可正常处理,此时返回 − 1 -1 1。否则,超过 v a l u e value value 的客户端调用量需要被限制在 v a l u e value value,而未超过 v a l u e value value 的客户端可以正常请求。要求计算可以接受的最大 v a l u e value value,以便尽可能地满足更多的请求。

输入格式

第一行包含 n n n 个空格分隔的正整数 R 1 , R 2 , . . . , R n R_1, R_2, ..., R_n R1,R2,...,Rn,表示每个客户端在一定时间内发送的交易请求数量。

第二行包含一个正整数 c n t cnt cnt,表示购物系统的最大调用量。

输出格式

输出一个整数,表示系统能够接受的最大请求调用量的阈值 v a l u e value value

样例输入

1 4 2 5 5 1 6
13

样例输出

2

解释说明

若将 v a l u e value value 设置成 6 6 6 1 + 4 + 2 + 5 + 5 + 1 + 6 > 13 1+4+2+5+5+1+6>13 1+4+2+5+5+1+6>13 不符合。

v a l u e value value 设置为 2 2 2 1 + 2 + 2 + 2 + 2 + 1 + 2 = 12 < 13 1+2+2+2+2+1+2=12<13 1+2+2+2+2+1+2=12<13 符合。

可以证明 v a l u e value value 最大为 2 2 2

评测数据与规模

  • 0 < n ≤ 1 0 5 0 < n \le 10^5 0<n105
  • 0 ≤ R i ≤ 1 0 5 0 \le R_i \le 10^5 0Ri105
  • 0 ≤ c n t ≤ 1 0 9 0 \le cnt \le 10^9 0cnt109

题解

我们先来判断 题目答案是否有 单调性

  1. 当阈值 v a l u e value value 变小时,请求量会被限制会变多,总的请求量就会减小
  2. 当阈值 v a l u e value value 变大时,请求量会被限制会变少,总的请求量就会变大.

所以 v a l u e value value 最大,答案最大,因此答案具有 单调性

因此我们可以尝试 一个答案 x x x

  • 设置 x x x 的左右边界, 左边界可以设置为 0 0 0 ,右边界最大为 m a x ( R i ) max(R_i) max(Ri),其中 0 < i < n 0 < i < n 0<i<n
  • 每次通过 x x x ,对当前的请求量求和,并和 c n t cnt cnt 做比较。
  • 判断当前 x x x 是否满足条件,如果满足,则尝试猜更大的 x x x,即跟新(右移)左端点
  • 因为是右移左端点,所以这里采用二分的 第二个模版

时间复杂度: O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))

AC代码

  • Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        List<Integer> list = new ArrayList<>();
        
        // 读取所有输入的整数
        while (sc.hasNextInt()) {
            int x = sc.nextInt();
            list.add(x);
        }

        int[] nums = new int[list.size()];
        
        // 将List转为数组
        for (int i = 0; i < list.size(); i++) {
            nums[i] = list.get(i);
        }

        int left = 0;
        int maxv = Arrays.stream(nums).max().getAsInt(); // 找到数组中的最大值
        int right = maxv;

        // 二分查找最大值
        while (left < right) {
            int mid = (left + right + 1) / 2;
            if (check(nums, mid)) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }

        // 输出结果
        if (right == maxv) {
            System.out.println(-1);
        } else {
            System.out.println(left);
        }
    }

    // 检查是否满足条件
    private static boolean check(int[] nums, int value) {
        long sum = 0;
        for (int i = 0; i < nums.length - 1; i++) {
            sum += Math.min(nums[i], value);
        }
        return sum <= nums[nums.length - 1];
    }
}

  • Cpp

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    bool check(vector<int>& nums, int value) {
        long long sum = 0;
        for (int i = 0; i < nums.size() - 1; ++i) {
            sum += min(nums[i], value);
        }
        return sum <= nums.back(); // nums.back() 返回最后一个元素
    }
    
    int main() {
        vector<int> nums;
        int x;
        
        // 读取所有输入的整数
        while (cin >> x) {
            nums.push_back(x);
        }
    
        int left = 0;
        int maxv = *max_element(nums.begin(), nums.end()); // 找到数组中的最大值
        int right = maxv;
    
        // 二分查找最大值
        while (left < right) {
            int mid = (left + right + 1) / 2;
            if (check(nums, mid)) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
    
        // 输出结果
        if (right == maxv) {
            cout << -1 << endl;
        } else {
            cout << left << endl;
        }
    
        return 0;
    }
    
    
  • Python

    def check(nums, value):
        total = 0
        for num in nums[:-1]:  # 排除最后一个元素
            total += min(num, value)
        return total <= nums[-1]  # 检查条件是否满足
    
    def main():
        import sys
        input = sys.stdin.read
        nums = list(map(int, input().split()))  # 读取所有输入的整数
    
        left = 0
        maxv = max(nums)  # 找到数组中的最大值
        right = maxv
    
        # 二分查找最大值
        while left < right:
            mid = (left + right + 1) // 2
            if check(nums, mid):
                left = mid
            else:
                right = mid - 1
    
        # 输出结果
        if right == maxv:
            print(-1)
        else:
            print(left)
    
    if __name__ == "__main__":
        main()
    
    
  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值