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. 转变数组后最接近目标值的数组和

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