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 的方案。

解題報告

類似於這種 最最問題 一般採用二分法。
左邊界爲 0,右邊界爲 max(arr)
在二分的過程中,一直更新最接近的那個值 mid,如果兩個不同的值,其均爲最接近的值,則記錄較小值。
具體代碼爲:

if(abs(dis)<MinDis){
    MinDis=abs(dis);
    ans=mid;
}
else if(abs(dis)==MinDis){
    ans=min(ans, mid);
}

當然了,這題還有優化空間,即左右邊界移動的步長。

 if(dis>0) r=mid-1;
 else l=mid+1;

改爲:

 if(dis>0){
     if(dis/n>0&&mid-dis/n>=l) r=mid-dis/n;
     else r=mid-1;
}
else{
     if(-dis/n>0&&mid-dis/n<=r) l=mid-dis/n;
     else l=mid+1;
}

當前數組和 和 目標值差的較大時,考慮增大步長,滿足的條件爲 dis/n>0 且 不會跨越 另一邊的邊界。因爲一旦跨越另一邊的邊界,則會跳出循環,有可能錯過最接近的解。

實現代碼

class Solution {
public:
    int findBestValue(vector<int>& arr, int target) {
        int n=arr.size(), MAX=INT_MIN;
        for(int i=0;i<n;i++){
            MAX=max(arr[i], MAX);
        }
        int l=0,r=MAX, mid, MinDis=INT_MAX, ans;
        while(l<=r){
            mid=l+(r-l)/2;
            int dis=match(mid, arr, target);
            if(abs(dis)<MinDis){
                MinDis=abs(dis);
                ans=mid;
            }
            else if(abs(dis)==MinDis){
                ans=min(ans, mid);
            }
            if(dis>0) r=mid-1;
            else l=mid+1;
			// 時間優化
            // if(dis>0){
            //     if(dis/n>0&&mid-dis/n>=l) r=mid-dis/n;
            //     else r=mid-1;
            // }
            // else{
            //     if(-dis/n>0&&mid-dis/n<=r) l=mid-dis/n;
            //     else l=mid+1;
            // }
        }
        return ans;
    }
    int match(int mid, vector<int>& arr, int target){
        int sum=0;
        for(int i=0;i<arr.size();i++){
            sum+=(arr[i]>mid?mid:arr[i]);
        }
        return sum-target;
    }
};

參考資料

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

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