每日打卡:转变数组后最接近目标值的数组和
打卡: 转变数组后最接近目标值的数组和
心情
周末刷完了《十日游戏》,关于影子与公主的爱情故事,看到了爱情、欺骗、谎言、赎罪。特别喜欢东野圭吾的悬疑小说,还有"于海" 简直是技术宅中的神~。
读题
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的判断我是想不到的!!!
- (9)1300. 转变数组后最接近目标值的数组和
- 【每日打卡】Day28:最接近的三数之和 C++实现
- 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
- 从一个数组中查找和目标数最接近的或者相等的数
- 每日算法(二十)-java给定一个数组,求是否存在数组里的四个元素相加等于目标数
- 每日算法(二十一)-java给定一个数组,求是否存在数组里的两个元素相加等于目标数并求出下标(三种方法)
- 求无序数组中三个元素相加与目标数最接近的三元素之和
- java折半查找法 查找数组中与目标数最接近的数
- 每日算法(十三)-java从一个有序数组中找出目标元素在数组中的出现的首次和最后一次位置
- 346/5000 给定一个n个整数的数组S,在S中找到三个整数,使得总和最接近给定数量的目标。 返回三个整数的和。
- 剑指offer打卡8-数组中出现次数超过一半的数字(java版)
- 判断一个数最接近数组中的某个数
- php 将数组导成excel时,字段(1/10)与日期接近的字符串会变excel自动转成10月1号
- 每日一练-----返回乘积最大的子数组的积
- POJ 3321 Apple Tree 【树形结构转变为线性结构+线段树OR树状数组】
- 每日一道算法题:数组中出现次数超过一半的数字
- leetcode每日一题-974. 和可被 K 整除的子数组
- 【每日打卡】Day25:字串非重复字符数排序 C++实现
- leetcode(js)-每日一练之 数组中重复的数据 题库编号442
- leetcode5-27每日一题:和可被 K 整除的子数组