Python世界:力扣题解1300,转变数组后最接近目标值的数组和,中等
- 任务背景
- 思路分析
- 代码实现
- 测试套件
- 本文小结
任务背景
问题来自力扣题目1300. Sum of Mutated Array Closest to Target,大意如下:
Given an integer array
arr
and a target valuetarget
, return the integervalue
such that when we change all the integers larger thanvalue
in 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之差绝对值最小。