力扣【二分查找】:1300. 转变数组后最接近目标值的数组和
- 题目描述:
给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近 target (最接近表示两者之差的绝对值最小)。
如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。
请注意,答案不一定是 arr 中的数字。
- 算法思路:
题目要求我们找到使得替换所有大于value的数之后,arr的数组和与target最接近的value,那么我们可以先确认value的范围,
由于题目给定的限制条件:1 <= arr.length <= 10^4,1 <= arr[i], target <= 10^5,那么如果value < 0,那么数组arr求和的值和target的绝对值只会变大,
所以value一定大于等于0,设想这样一个数组,它的长度很大使得即便value为1时它的数组和与target的绝对值依然大于target本身,那么这是value = 0就是绝对值最小的情况,
又因为可以替换为value的是arr中大于value的值,如果value 大于max(arr),则不会替换任何元素,也不会缩小绝对值,
所以value的最大值应该是max(arr),
因此0 <= value <= max(arr),这里具有有序性,所以可以使用二分查找的方式,
在每次二分时遍历数组计算arr和,
如果arr和小于target,则说明value可以变大,反之如果arr和大于target,则value可以减小,这样不断向绝对值0的反向趋近,
value只在绝对值结果变小时更新,如果绝对值相同则value等于新旧value中较小的一个,
- 代码:
时间复杂度:设二分边界为m,数组长度为n,O(n log (m))
int findBestValue(vector<int>& arr, int target) {
int lower_bound = 0, upper_bound = *std::max_element(arr.begin(), arr.end());
int result = *std::max_element(arr.begin(), arr.end());
int abs = std::abs(target - std::accumulate(arr.begin(), arr.end(), 0));
while (lower_bound < upper_bound) {
int mid = lower_bound + (upper_bound - lower_bound) / 2;
int sum = 0;
for (const int &n : arr) {
if (n > mid) {
sum += mid;
} else {
sum += n;
}
}
if (sum < target) {
lower_bound = mid + 1;
} else {
upper_bound = mid;
}
int new_abs = std::abs(sum - target);
if (new_abs < abs) {
abs = new_abs;
result = mid;
} else if (new_abs == abs) {
result = std::min(mid, result);
}
}
return result;
}
浙公网安备 33010602011771号