一. 题目分析
该题是一道兼具二分查找和前缀和的组合题,涉及数组排序和高效查询的组合应用。题目描述如下:
M 正在叉春旅游,想要打包尽可能多的巧克力板。每块巧克力板的边长为 a[i],重量为 a[i]^2。M 有多个背包,每个背包有一个最大承重限制 queries[j]。对于每个背包,我们需要确定在不超过承重限制的情况下,最多可以装上多少块巧克力板。
输入说明:
n:巧克力板的数量。m:背包的数量。a:巧克力板的边长数组。queries:背包的最大承重限制数组。
输出说明:对于每个背包,输出一个整数,表示最多可以装上的巧克力板数量。
二. 解题思路
第一步:计算巧克力重量
每块巧克力板的重量可以通过边长的应用计算得到:
weight[i] = a[i]^2
第二步:排序和前缀和
为了高效处理重量查询:
-
将巧克力重量排序为升序。
-
计算前缀和数组,以便快速获取多块巧克力板的积累重量。前缀和数组
prefix[i]定义为:prefix[i] = Σ (weight[j])对于j从 0 到i
第三步:二分查找
对于每个背包的承重限制,需要找出最多可以装上多少块巧克力板:
- 在前缀和数组中使用二分查找到第一个超过这个承重限制的位置。
- 该位置之前的所有巧克力板都在承重范围内。
第四步:优化考虑
- 排序和前缀和:通过将巧克力重量排序,使我们可以利用二分查找,加快解决。
- 二分查找:这一步可以大幅减少确定最多装上的巧克力板的时间。
三. 算法步骤
-
计算重量:根据巧克力板的边长计算每个巧克力板的重量。
-
排序和前缀和:
- 将重量数组按升序排序。
- 计算前缀和数组,记录到每个点的积累重量。
-
二分查找:
- 对于每个背包的承重限制,使用二分查找到第一个超过这个限制的前缀和。
-
输出结果:返回每个背包可以装上的巧克力板数量。
四. 示例分析
输入示例:
n = 5, m = 5, a = [1, 2, 2, 4, 5], queries = [1, 3, 7, 9, 15]
详细步骤:
-
计算巧克力重量:
weights = [1, 4, 4, 16, 25] -
排序 (升序排序) :
sorted_weights = [1, 4, 4, 16, 25] -
前缀和计算:
prefix = [1, 5, 9, 25, 50] -
查询结果:
queries[0] = 1:二分查找得到prefix[0] = 1,结果为1。queries[1] = 3:二分查找得到prefix[0] = 1,结果为1。queries[2] = 7:二分查找得到prefix[1] = 5,结果为2。queries[3] = 9:二分查找得到prefix[2] = 9,结果为3。queries[4] = 15:二分查找得到prefix[2] = 9,结果为3。
输出:[1, 1, 2, 3, 3]
五. 代码实现
下面是完整的 Python 实现:
def solution(n: int, m: int, a: list, queries: list) -> list:
# write code here
weights = [x ** 2 for x in a]
weights.sort()
prefix = [0] * n
prefix[0] = weights[0]
for i in range(1, n):
prefix[i] = prefix[i - 1] + weights[i]
result = []
for max_weight in queries:
left, right = 0, n - 1
while left <= right:
mid = (left + right) // 2
if prefix[mid] <= max_weight:
left = mid + 1
else:
right = mid - 1
result.append(right + 1)
return result
六. 复杂度分析
-
时间复杂度:
- 重量计算:
O(n) - 排序:
O(nlogn) - 前缀和计算:
O(n) - 二分查找:每次查询
O(logn),总共m次查询:O(mlogn) - 总时间复杂度:
O(nlog n + mlogn)
- 重量计算:
-
空间复杂度:
- 存储重量数组和前缀和数组:
O(n)
- 存储重量数组和前缀和数组:
七. 个人思考与总结
- 核心解决方案:解决这个问题的关键在于利用排序和前缀和来优化重量的计算。通过二分查找,确定可装巧克力板的数量复杂度从线性降低到对数级。
- 优化方向:如果背包承重范围超过很大,可考虑使用分块或者指数表进一步优化查询过程。