二分法的两个例子

问题一:转变数组后最接近目标值的数组和(leetcode1300)

问题描述:

给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近  target (最接近表示两者之差的绝对值最小)。

如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。

请注意,答案不一定是 arr 中的数字。

示例 1:

输入:arr = [4,9,3], target = 10
输出:3
解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。
示例 2:

输入:arr = [2,3,5], target = 10
输出:5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-mutated-array-closest-to-target
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解决方案:

由题可知,value值越大转化后数组和也就越大。先找到value上下界,再按照该规律二分查找数组和最接近target的value值;下界为0,上界为max(arr)

若此时给定一个value得到一个数组和,若该数组和大于等于target,说明解在该值的左边

若小于target时,最优解在该值的右边。

注:由于想要找到最小的value所以等于时应在其之前继续寻找

实现代码如下:

class Solution {
    public int findBestValue(int[] arr, int target) {
        int left = 0;
        int right = Integer.MIN_VALUE;
        for(int num : arr){
            right = Math.max(right, num);
        }
        while(left < right - 1){
            int mid = left + (right - left) / 2;
            if(getSum(arr, mid) >= target){
                right = mid;
            }else{
                left = mid;
            }
        }
        if(Math.abs(getSum(arr, left) - target) <= Math.abs(getSum(arr, right) - target)){
            return left;
        }
        return right;
    }
    public int getSum(int[] arr, int val){
        int sum = 0;
        for(int num : arr){
            sum += num > val ? val : num;
        }
        return sum;
    }
}

问题二:制作m束花所需的最小天数(leetcode5438)

问题描述:

给你一个整数数组 bloomDay,以及两个整数 m 和 k 。

现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。

花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。

请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-number-of-days-to-make-m-bouquets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解决方案:

辛亏今早周赛前把每日一题(问题一)做了,然后做这个问题亦很容易的想到二分+贪心的策略。由题意可知,当t天可以摘m束花时,则比t大的天数亦可以摘m束花,根据这一规律,先找到上下界(上界为max(bloomDay), 下界为min(bloomDay)),然后采用二分查找,此外判断给定的天数能否摘m束花时,就是看当前天数下,是否有m对k个一连的花。

代码如下:

class Solution {
    public int minDays(int[] bloomDay, int m, int k) {
        if(bloomDay.length < m * k){
            return -1;
        }
        int left = bloomDay[0];
        int right = bloomDay[0];
        for(int num : bloomDay){
            left = Math.min(left, num);
            right = Math.max(right, num);
        }
        while(left < right - 1){
            int mid = left + (right - left) / 2;
            if(process(bloomDay, m, k, mid)){
                right = mid;
            }else{
                left = mid;
            }
        }
        if(process(bloomDay, m, k, left)){
            return left;
        }
        return right;
    }
    public boolean process(int[] bloomDay, int m, int k, int day){
        int i = 0;
        while(i < bloomDay.length){
            for(int j = 0; j < k && i < bloomDay.length; j++, i++){
                if(bloomDay[i] > day){
                    i++;
                    break;
                }
                if(j == k - 1 && bloomDay[i] <= day){
                    m--;
                }
            }
        }
        // System.out.println(day + " " + m);
        return m <= 0;
    }
}

ps:这两个问题都在找满足条件的最小值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章