一天一大 leet(转变数组后最接近目标值的数组和)难度:中等 DAY-14

2023-10-10 03:50

本文主要是介绍一天一大 leet(转变数组后最接近目标值的数组和)难度:中等 DAY-14,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

20200614

img

题目(难度:中等):

给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近  target (最接近表示两者之差的绝对值最小)。

如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。

请注意,答案不一定是 arr 中的数字。

示例

  1. 示例 1
输入:arr = [4,9,3], target = 10
输出:3
解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。
  1. 示例 2
输入:arr = [2,3,5], target = 10
输出:5
  1. 示例 3
输入:arr = [60864,25176,27249,21296,20204], target = 56803
输出:11361

提示

  • 1 <= arr.length <= 1 0 4 10^4 104
  • 1 <= arr[i], target <= 1 0 5 10^5 105

抛砖引玉

img

  1. 当arr中数据都替换成的最大值时都小于target是返回最大值
  2. 循环arr的平均值到arr的最大值分别计算替换后数组的和
  • 小于平均数的和+指针之前的数的和(大于平均数的地方)
  • 计算和与target之前的差,每次比较上一次差与这次的大小,取最小值
/*** @param {number[]} arr* @param {number} target* @return {number}*/
var findBestValue = function(arr, target) {var r = arr[len - 1];if (r*len <= target) return r;arr.sort((a, b) => a - b);var len = arr.length,prefix = Array(len + 1).fill(0),aver = Math.floor(target / len),_result = 0,diff = targetindex = arr.includes(aver) ? arr.indexOf(i) : 0;for (var i = 1; i <= len; i++) {arr[i - 1] < aver ? index = i : nullprefix[i] = prefix[i - 1] + arr[i - 1];}for (var i = aver; i <= r; i++) {if (arr.includes(i)) {index = arr.indexOf(i) + 1;}var cur = prefix[index] + (len - index) * i;if (Math.abs(cur - target) < diff) {_result = i;diff = Math.abs(cur - target);}}return _result;
};

img

  1. 数组递增排序
  2. 记录每个数字对应的和目标值差值的平均值
  • 当这个数据大于平均值则说明符合条件的数字出现了
  • 因为之后的数据在计算时需要更新为返回值,则此时返回值与当前这个数据越接近则最终求的和越接近
  • 满足条件的最小整数,则四舍五入时舍弃0.5
/*** @param {number[]} arr* @param {number} target* @return {number}*/
var findBestValue = function(arr, target) {if (arr == null) {return 0;}arr.sort((a, b) => a - b);var arrSize = arr.length;var sum = 0;for (var i = 0; i < arrSize; i++) {var x = (target - sum) / (arrSize - i);if (x < arr[i]) {return Math.round(x - 0.1);}sum += arr[i];}return Math.round(arr[arrSize - 1]);
}

官方答案

class Solution:def findBestValue(self, arr: List[int], target: int) -> int:arr.sort()n = len(arr)prefix = [0]for num in arr:prefix.append(prefix[-1] + num)r, ans, diff = max(arr), 0, targetfor i in range(1, r + 1):it = bisect.bisect_left(arr, i)cur = prefix[it] + (n - it) * iif abs(cur - target) < diff:ans, diff = i, abs(cur - target)return ans

其他解法

先将数组arr排序,然后开始遍历。

使用变量sum_cur保存sum(sorted[0…i)]的值,用sum_pre
保存sum(sorted[0…i-1])的值,通过这两个变量循环往后计算求和。

首先,我们计算假如将所有数变成同一个数,这个value是多少,
这个初始的value不能大于arr中最小的数,因为如果如此,
就一定有数不能被它替代。

我们将这个数求和得到的结果存入approx中,作为以后对比的依据。

在此后的每次循环中,计算此时是否如果将所有sorted[i]及之后的数
全部替代,使得结果等于target。这个数同样不应该大于sorted[i],
不然sorted[i]就不能被替代。

此处要注意,假如在sorted[i-1]到sorted[i]之间存在新的value,
其小数等于0.5怎么办,如果出现了0.5那么说明剩余数的数量必然
是2的倍数,将其舍去或计入都会与target存在2的误差,为了使最后的
整数最小,应当舍去。方便起见,在Round前加一个小数解决。

假如这样求得的结果已经小于sorted[i-1]了,那么上一个数是结果。

/*** @param {number[]} arr* @param {number} target* @return {number}*/
var findBestValue = function (arr, target) {let sorted = arr.sort((x,y)=>x-y);let sums_cur = sorted[0];let sums_pre = -1;let value = Math.round(target / sorted.length);value = Math.min(value, sorted[0]);for (let i = 1; i < sorted.length; i ++){sums_pre = sums_cur;sums_cur = sums_pre + sorted[i];let new_value = Math.round((target - sums_pre) / (sorted.length - i) - 0.000001);new_value = Math.min(new_value, sorted[i]);if (sorted[i-1] > new_value){break;}value = new_value;}return value;
}
  1. 数组先排序,为了不断计算数组和的时候比较方便
  2. 二分查找,找到使数组和最接近 target 的 value,二分查找的时候让左边界收缩,最终拿到的
    right 就是最接近的右边界,但是最终还要比较一下 right 和 right - 1 哪一个更接近
    如果 right - 1 和 right 计算的数组和与 target 的绝对值相等呢?那么返回 right - 1,
    题目要求是返回尽可能小的那个数
var findBestValue = function(arr, target) {let len = arr.length;arr = arr.sort((a, b) => a - b);let ans;// 计算前缀和,优化计算数组和的时间let prefixSum = [], tempSum = 0;prefixSum[-1] = 0;for (let i = 0; i < len; i++) {tempSum += arr[i];prefixSum[i] = tempSum;}// 二分查找范围是:0 ~ max(arr) let left = 0, right = arr[len - 1], mid;while (left < right) {mid = left + ((right - left) >> 1);let sum = calculateSum(arr, mid, len, prefixSum);if (sum >= target) {right = mid;} else {left = mid + 1;}}// 比较 right 和 right - 1 两个数,哪一个使数组和最接近 target,返回它// 这里比较 left 和 left - 1 也行,因为上面的二分结束时,left 和 right 是相等的let rightSum = calculateSum(arr, right, len, prefixSum),beforeRightSum = calculateSum(arr, right - 1, len, prefixSum);let diffRight = Math.abs(rightSum - target),diffBeforeRight = Math.abs(beforeRightSum - target);return diffBeforeRight <= diffRight ? right - 1 : right;
};// 使用前缀和,计算「使得将数组中所有大于 value 的值变成 value 后」的和
function calculateSum(arr, mid, len, prefixSum) {let sum = 0, i = 0;while (i < len) {if (arr[i] > mid) break;i++;}sum = (len - i) * mid + prefixSum[i-1];return sum;
}
  1. 将数组arr按升序排序
  2. 用remain存储与target值还差多少
  3. 遍历arr过程中,计算tmp = remain / N - i,即达到目标值需要后面至少是N-i个tmp值,值得注意的是在js中/得到的是浮点数。
    1.当平均值tmp比当前值arr[i]小的时候,说明把当前下标i及后边的元素改成大于等于tmp的值时,最接近target。由于题目要求的是最小的value,所以tmp的小数点部位<=0.5时,都应该向下取整,反之向上取整。
    1.如果能够走完整个for循环,说明“target值很大”。所以原数组和就是距离target最近的值,所以直接返回arr[N - 1],即原数组的最大值。
    注: 关于“target值很大”的解释:首先按照题目的意思按照某个value值,用value替换掉大于value的值,这个做最后肯定是把整个数组和变小了。如果原数组的和小于target,那么按照某个value替换后更小于target了。
/*** @param {number[]} arr* @param {number} target* @return {number}*/
var findBestValue = function (arr, target) {arr.sort((a, b) => a - b);const N = arr.length;let remain = target;for (let i = 0; i < N; i++) {let tmp = remain / (N - i);if (tmp < arr[i]) {return (tmp - Math.floor(tmp)) <= 0.5 ? Math.floor(tmp) : Math.ceil(tmp); }remain -= arr[i];}return arr[N - 1];
}

博客: 小书童博客(http://gaowenju.com/)

公号: 坑人的小书童
坑人的小书童

这篇关于一天一大 leet(转变数组后最接近目标值的数组和)难度:中等 DAY-14的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/177740

相关文章

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

C语言:柔性数组

数组定义 柔性数组 err int arr[0] = {0}; // ERROR 柔性数组 // 常见struct Test{int len;char arr[1024];} // 柔性数组struct Test{int len;char arr[0];}struct Test *t;t = malloc(sizeof(Test) + 11);strcpy(t->arr,

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

计算数组的斜率,偏移,R2

模拟Excel中的R2的计算。         public bool fnCheckRear_R2(List<double[]> lRear, int iMinRear, int iMaxRear, ref double dR2)         {             bool bResult = true;             int n = 0;             dou

C# double[] 和Matlab数组MWArray[]转换

C# double[] 转换成MWArray[], 直接赋值就行             MWNumericArray[] ma = new MWNumericArray[4];             double[] dT = new double[] { 0 };             double[] dT1 = new double[] { 0,2 };

【多系统萎缩患者必看】✨维生素补充全攻略,守护你的健康每一天!

亲爱的朋友们,今天我们要聊一个既重要又容易被忽视的话题——‌多系统萎缩患者如何科学补充维生素‌!🌟 在这个快节奏的生活中,健康成为了我们最宝贵的财富,而对于多系统萎缩(MSA)的患者来说,合理的营养补充更是维护身体机能、提升生活质量的关键一步。👇 🌈 为什么多系统萎缩患者需要特别关注维生素? 多系统萎缩是一种罕见且复杂的神经系统疾病,它影响身体的多个系统,包括自主神经、锥体外系、小脑及锥