問題描述
給你一個整數數組 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;
}
};