二分法的兩個例子

問題一:轉變數組後最接近目標值的數組和(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:這兩個問題都在找滿足條件的最小值。

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