题目描述
给你一个整数数组 arr
和一个目标值 target
,请你返回一个整数 value
,使得将数组中所有大于 value
的值变成 value
后,数组的和最接近 target
(最接近表示两者之差的绝对值最小)。
如果有多种使得和最接近 target
的方案,请你返回这些整数中的最小值。
请注意,答案不一定是 arr
中的数字。
样例
输入:arr = [4,9,3], target = 10
输出:3
解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。
输入:arr = [2,3,5], target = 10
输出:5
输入:arr = [60864,25176,27249,21296,20204], target = 56803
输出:11361
限制
1 <= arr.length <= 10^4
1 <= arr[i], target <= 10^5
算法
(二分答案) $O(n \log M)$
- 我们不妨求出一个最小的
value
,满足大于等于target
,然后我们比较value - 1
和value
哪个更接近target
。 - 求满足大于等于
target
的最小值,显然可以采用二分答案然后判定的方式。如果当前二分的value
计算出来的结果小于target
,则继续向上二分。否则,向下二分。 - 二分的下界为 0,上界为数组的最大值。
时间复杂度
- 每次判定的时间复杂度为 $O(n)$,故总时间复杂度为 $O(n \log M)$,其中 $M$ 为最大值。
空间复杂度
- 仅需要常数的额外空间。
C++ 代码
class Solution {
public:
int calc(int v, const vector<int>& arr) {
int s = 0;
for (int x : arr)
s += min(v, x);
return s;
}
int findBestValue(vector<int>& arr, int target) {
int n = arr.size();
int l = 0, r = arr[0];
for (int i = 1; i < n; i++)
r = max(r, arr[i]);
while (l < r) {
int mid = (l + r) >> 1;
if (calc(mid, arr) < target)
l = mid + 1;
else
r = mid;
}
if (abs(calc(l - 1, arr) - target) <= abs(calc(l, arr) - target))
return l - 1;
return l;
}
};
最后一步判断 l - 1更优,具备证明行吗?是不是两个相同结果的方案之间最多差1
大佬题解里的”我们不妨XXX”
这种思维跳跃 不亚于”我们不妨直接写出答案 balabalabala…” hh