您的位置:首页 > 其它

每日打卡:转变数组后最接近目标值的数组和

2020-07-18 04:22 155 查看

打卡: 转变数组后最接近目标值的数组和

心情

周末刷完了《十日游戏》,关于影子与公主的爱情故事,看到了爱情、欺骗、谎言、赎罪。特别喜欢东野圭吾的悬疑小说,还有"于海" 简直是技术宅中的神~。

读题

leetcode: 1300. 转变数组后最接近目标值的数组和

描述:
给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近 target (最接近表示两者之差的绝对值最小)。
如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。
请注意,答案不一定是 arr 中的数字。

测试用例:
输入:arr = [4,9,3], target = 10
输出:3
输入: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

虽说标签中提到了二分查找,但是感觉完全没有必要呀。

思路

1:先排序,从小到大
2:找到临界的那个数
3:将临界的那个数减去超出的部分
4:判断符合条件的最小值

实现

public int findBestValue(int[] arr, int target) {
//先排序
Arrays.sort(arr);
int sum = 0, temp = 0, i;
for (i = 0; i < arr.length; i++) {
temp = sum + (arr.length - i) * arr[i];
//找到临界数据 arr[i]
if (temp > target) {
//若超过了target,则将超过的数值赋给temp
temp = temp - target;
break;
} else if (temp == target || i == arr.length - 1) {
//若是刚好相等或者最后一个值也无法达到target,直接返回
return arr[i];
}
sum += arr[i];
}
//通过arr[i]-单个值超出的部分得到结果。考虑到小数,会有两种结果
int left = arr[i] - (int) Math.ceil(temp * 1.0 / (arr.length - i));
int right = arr[i] - (int) Math.floor(temp * 1.0 / (arr.length - i));
if(left == right) return left;
//right==left+1,此时比较那个结果距离target更近,若是一样近则取left
return (sum * 2 + (left + right) * (arr.length - i)) / 2 < target ? right : left;
}

感觉还是很好理解的,考虑到前面的数字必须要求和,无法避免for循环。

提交


我试着优化了几次,按照这个思路已经优化到极限了。看下官方怎么使用二分法来处理的吧。

标答

public int findBestValue(int[] arr, int target) {
Arrays.sort(arr);
int len = arr.length;
int curSum = 0;
for (int i = 0; i < len; i++) {
//为了满足target,整数的解为curAve
int curAve = (target - curSum) / (len - i);
if (curAve <= arr[i]) {
//到达了临界点,此时带小数的解为curAveDou
double curAveDou = (target * 1.0 - curSum) / (len - i);
if (curAveDou - curAve <= 0.5) {
//这个0.5比较难理解
return curAve;
} else {
return curAve + 1;
}
}
curSum += arr[i];
}
//找不到临界点则返回最大值
return arr[len - 1];
}

标答取自评论区某大神
那个0.5我还是没办法理解,不过可以推导出来。
在我的解法中,结果为 (sum * 2 + (left + right) * (arr.length - i)) / 2 < target ? right : left 看平均值在target的左边还是右边,来判断哪个解更接近目标值。
取left的值需要满足:target >= (sum * 2 + (left + right) * (arr.length - i)) / 2
target 2 - sum2 >= (left + right) * (arr.length - i)
arr.length-i必然是大于0的,right=left+1,不然也不会走到这一步。
(target - sum)/(arr.length - i) >= (left+right)/2
不等书左侧公式=题解中的curAveDou
不等式右侧公式中left=题解中的curAve
curAveDou*2>=curAve+curAve+1
curAveDou-curAve>=0.5
由上述的推导过程,若需要取left,则需要满足curAveDou-curAve>=0.5
只能说大神还是厉害呀,这个0.5的判断我是想不到的!!!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐