【题型总结】二分答案之最大化最小化

文章介绍了如何利用二分查找算法解决巧克力分割问题,目标是最大化保留给自己吃的巧克力的甜度。通过分析问题的单调性,可以将问题转化为在固定分割次数下寻找最小甜度的最大值。二分查找在前缀和数组上进行,判断是否能按要求分割,最终找到最佳切割策略。文章还讨论了类似问题,如球的磁力和礼盒甜蜜度问题,同样利用二分查找方法找到最大最小值。
摘要由CSDN通过智能技术生成

最大化最小值

分享巧克力【LC1231】

You have one chocolate bar that consists of some chunks. Each chunk has its own sweetness given by the array sweetness.

You want to share the chocolate with your k friends so you start cutting the chocolate bar into k + 1 pieces using k cuts, each piece consists of some consecutive chunks.

Being generous, you will eat the piece with the minimum total sweetness and give the other pieces to your friends.

Find the maximum total sweetness of the piece you can get by cutting the chocolate bar optimally.

你有一大块巧克力,它由一些甜度不完全相同的小块组成。我们用数组 sweetness 来表示每一小块的甜度。

你打算和 K 名朋友一起分享这块巧克力,所以你需要将切割 K 次才能得到 K+1 块,每一块都由一些 连续 的小块组成。

为了表现出你的慷慨,你将会吃掉 总甜度最小 的一块,并将其余几块分给你的朋友们。

请找出一个最佳的切割策略,使得你所分得的巧克力 总甜度最大,并返回这个 最大总甜度

  • 思路:

    • 题意:将数组sweetness分割成连续的k+1个子数组,返回分割后的子数组中和最小的最大值。
    • 本题的单调性为:当**最小甜度【连续子数组和的最小值】**最大,最多分割的份数越小,那么可以通过二分查找法找到最终答案
    • 将题意转化为,当分割份数一定时,寻找最小甜度的最大值 y y y,那么可以通过二分查找的方法找到最终的 y y y,二分查找的下限为1,上限为原始数组的分成 k + 1 k+1 k+1份后的平均值
    • 二分查找的对象不是甜度数组,而是前缀和数组,为递增数组
  • 实现

    • 首先统计前缀和数组,快速计算连续子数组的和【也可以不计算 将累加值存储在变量中】
    • 当二分查找最小甜度 y y y时,需要判断能否将数组分割成 k + 1 k+1 k+1个子数组,如果能够分割成 k + 1 k+1 k+1个子数组,那么我们可以增大子数组和,以获得更大的最小甜度;如果不能放下m个球,那么减少子数组和
    • 如何判断能否分割成 k + 1 k+1 k+1个子数组?
      • 需要使每个子数组的和均大于等于最小甜度 y y y,那么可以从第一个元素开始分割合法的子数组使,得到最终的分割个数 c o u n t count count,若$count \ge k+1 $,那么表示可以分割
    class Solution {
        public int maximizeSweetness(int[] sweetness, int k) {
            int n = sweetness.length;
            int[] preSum = new int [n + 1];
            for (int i = 0; i < n; i++){
                preSum[i + 1] = preSum[i] + sweetness[i];
            }
            int l = 1, r = preSum[n] / (k + 1);
            int ans = 0;
            while (l <= r){
                int mid = (l + r) / 2;
                if (check(mid, preSum, k + 1)){
                    ans = mid;
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
            return ans;
        }
        public boolean check(int min, int[] preSum, int k){
            int count = 0;
            int preIndex = 0;
            for (int i = 1; i < preSum.length; i++){
                if (preSum[i] - preSum[preIndex] >= min){
                    count++;
                    preIndex = i;
                }
            }
            return count >= k;
        }
    }
    
    • 时间复杂度: O ( n + n l o g ( C ) ) O(n+nlog(C)) O(n+nlog(C)) n n n是数组的长度, C C C是原始数组的分成 k + 1 k+1 k+1份后的平均值。二分查找的时间复杂度是 O ( l o g C ) O(logC) O(logC),每次二分查找需要判断是否符合条件的时间复杂度为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n + n l o g C ) O(n+nlogC) O(n+nlogC)
    • 空间复杂度: O ( n ) O(n) O(n),前缀和数组需要的空间
  • 实现:不使用累加和数组

    class Solution {
        public int maximizeSweetness(int[] sweetness, int k) {
            int n = sweetness.length;
            int sum = 0;
            for (int i = 0; i < n; i++){
                sum += sweetness[i];
            }
            int l = 1, r = sum / (k + 1);
            int ans = 0;
            while (l <= r){
                int mid = (l + r) / 2;
                if (check(mid, sweetness, k + 1)){
                    ans = mid;
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
            return ans;
        }
        public boolean check(int min, int[] sweetness, int k){
            int count = 0;
            int sum = 0;
            for (int i = 0; i < sweetness.length; i++){
                sum += sweetness[i];
                if (sum >= min){
                    count++;
                    sum = 0;
                }
            }
            return count >= k;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n + n l o g ( n C ) ) O(n+nlog(nC)) O(n+nlog(nC)) n n n是数组的长度,C是数组篮子位置中的最大值。求出前缀和数组需要的时间复杂度为 O ( n ) O(n) O(n),二分查找的时间复杂度是 O ( l o g C ) O(logC) O(logC),每次二分查找需要判断是否符合条件的时间复杂度为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n + n l o g C ) O(n+nlogC) O(n+nlogC)
      • 空间复杂度: O ( 1 ) O(1) O(1)

两球间的磁力【LC1552】

在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力 最大。

已知两个球如果分别位于 xy ,那么它们之间的磁力为 |x - y|

给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。

  • 思路:

    • 首先,二分查找的核心就是找到具有单调性的正确查找对象(来自北枝大佬)

      • 一般来说,查找对象就是题目要求的值,这里为最小磁力【最小球间距】

      • 本题的单调性为,最小磁力越大,最多能放置的球的个数越小

      那么,可以将题意转化为,当小球个数一定时,寻找最小磁力的最大值 y y y,那么可以通过二分查找的方法找到最终的 y y y,二分查找的下限为1,上限为最后一个位置与第一个位置的距离

  • 实现

    • 二分查找要求数组是升序排列的,因此我们需要将位置数组position排序,然后二分查找最终答案

    • 当二分查找最小磁力 y y y时,需要判断能否放下 m m m个球,如果能够放下m个球,那么我们可以增大球间距,以获得更大的最小磁力;如果不能放下m个球,那么减少球间距

    • 如何判断当前的球间距可以放下多少个球?

      模拟迭代,第一个小球放在 p o s i t i o n [ 0 ] position[0] position[0],第二个小球放在大于 p o s i t i o n [ 0 ] + y position[0]+y position[0]+y p o s i t i o n [ i ] position[i] position[i]处……

    • 思考:当在某一个最小磁力 y y y时,可能会有每一个小球间距均大于 y y y的情况,那么此时二分查找的下一步会增大球间距,小球的个数会减小,最大的最小磁力会增大,最后得到最优解

    class Solution {
        public int maxDistance(int[] position, int m) {
            Arrays.sort(position);
            int n = position.length;
            int l = 1, r = position[n - 1] - position[0];
            int ans = 0;
            while (l <= r){
                int mid = (l + r) / 2;
                if (check(mid, position, m)){
                    ans = mid;
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
            return ans;
        }
        public boolean check (int y, int[] position, int m){
            int count = 1;
            int pre = position[0];
            for (int i = 1; i < position.length; i++){
                if (position[i] - pre >= y){
                    count++;
                    pre = position[i];
                }
            }
            return count >= m;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n l o g ( n C ) ) O(nlog(nC)) O(nlog(nC)) n n n是数组的长度,C是数组篮子位置中的最大值。排序需要的时间复杂度为 O ( n l o g n ) O(nlog n) O(nlogn),二分查找的时间复杂度是 O ( l o g C ) O(logC) O(logC),每次二分查找需要判断是否符合条件的时间复杂度为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n l o g ( n c ) ) O(nlog(nc)) O(nlog(nc))
      • 空间复杂度: O ( l o g n ) O(logn) O(logn),排序所需要的栈空间

礼盒的最大甜蜜度【LC2517】

You are given an array of positive integers price where price[i] denotes the price of the ith candy and a positive integer k.

The store sells baskets of k distinct candies. The tastiness of a candy basket is the smallest absolute difference of the prices of any two candies in the basket.

Return the maximum tastiness of a candy basket.

  • 思路:使用二分查找的方式寻找最大甜蜜度

    • 本题的单调性为:当**最小甜蜜度【数组中任意两个元素的差值的绝对值的最小值】**越大,糖果的可以选择的种类越小【选取的数组中的元素个数】,因此可以通过二分查找法找到最终答案
    • 将题意转化为:当选取的数量一定时,寻找最小甜蜜度的最大值 y y y,二分查找的下限为0,上限为数组中最大元素与最小元素的差值/(k-1)
  • 实现

    • 首先对price数组进行升序排序

    • 然后进行二分查找,当二分查找最小甜蜜度 y y y时,需要判断能否取出 k k k种糖果,如果能取出 k k k种糖果,那么我们可以增大差值,已获得更大的最小甜蜜度;如果不能取出 k k k中糖果,那么减小差值

    • 每次查找成功,记录当前的最小甜蜜度,最后一次的最小甜蜜度即为最大最小甜蜜度

    • 如何判断当前的差值能够取出多少种糖果?

      模拟迭代,第一种糖果取 p r i c e [ 0 ] price[0] price[0],第一种糖果取 p r i c e [ 0 ] + y price[0]+y price[0]+y p r i c e [ i ] price[i] price[i]处……

  • 代码

    class Solution {
        public int maximumTastiness(int[] price, int k) {
            Arrays.sort(price);
            int n = price.length;
            int l = 0, r = price[n - 1] - price[0];
            int ans = 0;
            while (l <= r){
                int mid = (r + l) / 2;
                if (check(price, mid ,k)){
                    ans = mid;
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
            return ans;
    
        }
        public boolean check(int[] price, int m, int k){
            int count = 1;
            int preIndex = 0;
            for (int i = 1; i < price.length; i++){
                if (price[i] - price[preIndex] >= m ){
                    count++;
                    preIndex = i;
                }
            }
            return count >= k;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n l o g ( n C ) ) O(nlog(nC)) O(nlog(nC)) n n n是数组的长度,C是数组中的最大值与最小值的差值。排序需要的时间复杂度为 O ( n l o g n ) O(nlog n) O(nlogn),二分查找的时间复杂度是 O ( l o g C ) O(logC) O(logC),每次二分查找需要判断是否符合条件的时间复杂度为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n l o g ( n c ) ) O(nlog(nc)) O(nlog(nc))
      • 空间复杂度: O ( l o g n ) O(logn) O(logn),排序所需要的栈空间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值