題目描述
給你一個整數數組 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值進行遍歷:(參考LeetCode官方解題思路)
-
value
的下界爲 0。這是因爲當value
= 0 時,數組的和爲 0。由於target
是正整數,因此當value
繼續減小時,數組的和也會隨之減小,且變爲負數(這個和等於value * n
,其中 n 是數組arr
的長度),並不會比value
= 0 時更接近target
。 -
value
的上界爲數組arr
中的最大值。這是因爲當value >= arr
時,數組中所有的元素都不變,因爲它們均不大於value
。由於我們需要找到最接近target
的最小value
值,因此我們只需將數組arr
中的最大值作爲上界即可。
當我們確定了 value
值的上下界之後,就可以進行枚舉了。當枚舉到 value = x
時,我們需要將數組 arr
中所有小於等於 x
的值保持不變,所有大於 x
的值變爲 x
。要實現這個操作,我們可以將數組 arr
先進行排序,隨後進行二分查找,找出數組 arr
中最小的比 x
大的元素 arr[i]
。此時數組的和變爲:
具體代碼
class Solution {
public:
int findBestValue(vector<int>& arr, int target) {
//將數組進行排序(默認爲升序)
sort(arr.begin(), arr.end());
int n = arr.size();
vector<int> prefix(n + 1);
//計算前綴和
for (int i = 1; i <= n; ++i) {
prefix[i] = prefix[i - 1] + arr[i - 1];
}
//尋找數組中最大的元素,遍歷的範圍是從0到最大值。
int r = *max_element(arr.begin(), arr.end());
int ans = 0, diff = target;
for (int i = 1; i <= r; ++i) {
auto iter = lower_bound(arr.begin(), arr.end(), i);
int cur = prefix[iter - arr.begin()] + (arr.end() - iter) * i;
if (abs(cur - target) < diff) {
ans = i;
diff = abs(cur - target);
}
}
return ans;
}
};