[LeetCode]1300. 轉變數組後最接近目標值的數組和

題目

給你一個整數數組 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

示例 3:

輸入:arr = [60864,25176,27249,21296,20204], target = 56803
輸出:11361

提示:

  • 1 <= arr.length <= 10^4
  • 1 <= arr[i], target <= 10^5

解題思路

主要是通過二分法來找到一個合適的 value 值,使得它對應的數組之和 sum 是第一個大於等於 target 的,那麼最後實際的目標閾值可能是 value 也可能是 value - 1。
二分開始時 value 的上下界取值爲:
1)value 的下界爲 0。這是因爲當 value = 0 時,數組中的元素都變爲0,數組之和爲 0。由於 target 是正整數,因此當 value 繼續減小時,數組的和也會隨之減小,且變爲負數(這個和等於 value * n,其中 n 是數組 arr 的長度),並不會比 value = 0 時更接近 target。
2)value 的上界爲數組 arr 中的最大值。這是因爲當 value >= arr 時,數組中所有的元素都不變,因爲它們均不大於 value。由於我們需要找到最接近 target 的最小 value 值,因此我們只需將數組 arr 中的最大值作爲上界即可。
最後二分返回的value可能是,也可能不是,需要再進行一輪比較,選擇轉變後的數組之和 sum 更接近 target 的對應value。

複雜度分析:
時間複雜度:O(NlogN),這裏 N 是輸入數組的長度,二分的時間複雜度是 O(logN),每一次 calSum 的時間複雜度是 O(N)。
空間複雜度:O(1)。

代碼

class Solution {
    public int findBestValue(int[] arr, int target) {
        // 左端點爲0
        int left = 0;
        // 右端點爲數組中的最大值,這裏先定義爲0
        int right = 0;
        // 找數組中的最大值
        for (int num : arr) {
            right = Math.max(right, num);
        }
        // 找到第一個大於等於target的數組之和sum對應的閾值value,那麼目標閾值可能在 value 也可能在 value - 1。
        while(left<right){
            int mid = left + (right-left)/2;
            // 計算arr數組中所有元素變爲小於等於mid之後的總和
            int sum = calSum(arr, mid);
            if(sum<target){
                // 嚴格小於的一定不是解,threshold 太小了,下一輪搜索區間是 [mid + 1, right]
                left = mid + 1;
            }else{
                // 下一輪搜索區間是 [left, mid]
                right = mid;
            }
        }
        // 比較閾值線分別定在 left 和 left - 1 的時候與 target 的接近程度
        int sum1 = calSum(arr, left);
        int sum2 = calSum(arr, left-1);
        if((sum1 - target) < (target-sum2)){
            return left;
        }else{
            return left - 1;
        }
    }
    
    // 計算arr數組中所有元素變爲小於等於閾值threshold之後的總和
    private int calSum(int[] arr, int threshold){
        int sum = 0;
        for(int num : arr){
            sum += Math.min(num, threshold);
        }
        return sum;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章