Python世界:力扣题解1300,转变数组后最接近目标值的数组和,中等
- 任务背景
- 思路分析
- 代码实现
- 测试套件
- 本文小结
任务背景
问题来自力扣题目1300. Sum of Mutated Array Closest to Target,大意如下:
Given an integer array
arrand a target valuetarget, return the integervaluesuch that when we change all the integers larger thanvaluein the given array to be equal tovalue, the sum of the array gets as close as possible (in absolute difference) totarget.In case of a tie, return the minimum such integer.
Notice that the answer is not neccesarilly a number from
arr.
翻译下,转变数组后最接近目标值的数组和,实际需求是:对给定无序数组及目标值,返回一个value值使数组里大于该值的均置换为value,从而使新数组的元素和,最接近target。
思路分析
容易看到该value有个最小和最大值限制,最小值是target/len,最大值是数组的最大元素值max(arr)。
题意可以转化为找区间内,最小的value生成sum和,满足条件target的值,而且最小到最大值的sum是升序的。故容易看出,可以用二分法求边界值来处理。
思路如下:
- 先升序排序arr
- 遍历不同索引下arr的和数组sum
- sum结果分类有三种:
- value在有序数组左边,
arr[0]*n > target - value在有序数组右边,
sum[-1] <= target - value在有序数组中间,
以上条件以外
- value在有序数组左边,
- 前两种条件,可直接返回处理
- 第三种条件,需用二分法找最小的value
- 粗搜,先二分找数组元素的边界值
- 细搜,再二分找数组元素边界值左右侧中间值
代码实现
import numpy as np
class Solution(object):
def get_adapt_sum(self, arr_sorted, arr_sum, arr_len, mid):
sum1 = arr_sum[mid]
sum2 = arr_sorted[mid + 1] * (arr_len - (mid + 1))
sum_adapt = sum1 + sum2
return sum_adapt
def findBestValue(self, arr, target):
"""
:type arr: List[int]
:type target: int
:rtype: int
"""
arr_sorted = sorted(arr)
arr_len = len(arr)
arr_sum = np.zeros(arr_len)
# print(arr_sorted)
val_sum = 0
for i in range(arr_len):
val_sum += arr_sorted[i]
arr_sum[i] = val_sum
# 边界在右侧
if arr_sum[-1] < target:
return arr_sorted[-1]
# 边界在左侧
if arr_sorted[0]*arr_len > target:
low = target // arr_len
high = low + 1
sum_low = low * arr_len
sum_high = high * arr_len
if (abs(sum_low - target)) <= (abs(sum_high - target)):
return low
else:
return high
# 边界在中间
# search stage 1, range: [low, high)
low = 0
high = arr_len
while (low < high):
mid = low + (high - low) // 2
assert(mid != arr_len)
sum_adapt = self.get_adapt_sum(arr_sorted, arr_sum, arr_len, mid)
if (sum_adapt < target):
low = mid + 1
else: # sum_adapt >= target
high = mid
# search stage 2
base = arr_sum[high]
n = arr_len - (high + 1)
low = arr_sorted[high]
high = arr_sorted[high + 1]
while (low < high):
mid = low + (high - low) // 2
sum_cur = base + n * mid
if (sum_cur == target):
return mid
elif (sum_cur < target):
low = mid + 1
else:
high = mid
low = high - 1
sum_low = base + n * low
sum_high = base + n * high
if (abs(sum_low - target)) <= (abs(sum_high - target)):
return low
else:
return high
测试套件
用例设计有玄机,解析后分类如下:
结果在左侧边界
- ret = 3
- arr = [4,9,3]
- target = 10
结果在右侧边界
- ret = 5
- arr = [2,3,5]
- target = 10
结果在左侧外
- ret = 11361
- arr = [60864,25176,27249,21296,20204]
- target = 56803
最后,要注意增补下结果在中间的用例。
单例测试demo:
ret = 3
arr = [4,9,3]
target = 10
ret = 5
arr = [2,3,5]
target = 10
ret = 11361
arr = [60864,25176,27249,21296,20204]
target = 56803
sol = Solution()
res = sol.findBestValue(arr, target)
print(res)
测试套demo:
import unittest
def test_base(self, arr, target, ret):
sol = Solution()
res = sol.findBestValue(arr, target)
self.assertEqual(res, ret)
# 编写测试套
class TestSol(unittest.TestCase):
def test_special1(self):
ret = 3
arr = [4,9,3]
target = 10
test_base(self, arr, target, ret)
def test_special2(self):
ret = 11361
arr = [60864,25176,27249,21296,20204]
target = 56803
test_base(self, arr, target, ret)
def test_common1(self):
ret = 5
arr = [2,3,5]
target = 10
test_base(self, arr, target, ret)
def test_common2(self):
ret = 2
arr = [2,3,5]
target = 6
test_base(self, arr, target, ret)
def test_common3(self):
ret = 4
arr = [2,3,5]
target = 9
test_base(self, arr, target, ret)
# 测试套版本主调
if __name__ == '__main__':
print('start!')
unittest.main() # 启动单元测试
print('done!')
本文小结
本题的关键在于正确理解题意并转化到二分法的框架里,有序+上下界条件,最后是注意边界条件与目标结果的细节处理,要求的是sum和target之差绝对值最小。



















