Medium
题目描述
给你一个二维整数数组 items,其中 items[i] = [pricei, beautyi] 分别表示第 i 个物品的价格和美丽值。
同时给你一个下标从 0 开始的整数数组 queries。对于每个 queries[j],你想要确定价格小于等于 queries[j] 的物品中的最大美丽值。如果不存在这样的物品,那么查询的答案为 0。
返回一个与 queries 长度相同的数组 answer,其中 answer[j] 是第 j 个查询的答案。
示例 1:
输入:items = [[1,2],[3,2],[2,4],[5,6],[3,5]], queries = [1,2,3,4,5,6]
输出:[2,4,5,5,6,6]
解释:
- 对于 queries[0]=1,[1,2] 是唯一价格 <= 1 的物品。因此,这个查询的答案是 2。
- 对于 queries[1]=2,可以考虑的物品是 [1,2] 和 [2,4]。其中最大美丽值是 4。
- 对于 queries[2]=3 和 queries[3]=4,可以考虑的物品是 [1,2], [3,2], [2,4], 和 [3,5]。其中最大美丽值是 5。
- 对于 queries[4]=5 和 queries[5]=6,可以考虑所有物品。因此答案是所有物品中的最大美丽值,即 6。
示例 2:
输入:items = [[1,2],[1,2],[1,3],[1,4]], queries = [1]
输出:[4]
解释:每个物品的价格都等于 1,所以我们选择美丽值最大的物品 4。
注意,多个物品可以有相同的价格和/或美丽值。
示例 3:
输入:items = [[10,1000]], queries = [5]
输出:[0]
解释:没有价格小于等于 5 的物品,所以无法选择任何物品。因此查询的答案是 0。
约束条件:
1 <= items.length, queries.length <= 10^5items[i].length == 21 <= pricei, beautyi, queries[j] <= 10^9
解题思路
这道题的核心思想是预处理和二分查找的结合使用。
思路分析:
排序预处理:首先按价格对物品进行排序,这样我们可以利用有序性进行二分查找。
美丽值最大化:对于每个价格点,我们需要知道价格不超过该值的所有物品中的最大美丽值。我们可以在排序后遍历一遍,维护一个前缀最大美丽值数组。
去重优化:如果多个物品价格相同,我们只需要保留美丽值最大的那个,这样可以减少数据规模。
二分查找:对于每个查询,使用二分查找找到价格不超过查询值的最后一个物品位置,然后返回对应的最大美丽值。
算法步骤:
- 按价格排序items数组
- 处理相同价格的物品,只保留美丽值最大的
- 计算每个位置的前缀最大美丽值
- 对每个查询进行二分查找
时间复杂度主要来自排序和查询阶段的二分查找,整体效率很高。
代码实现
class Solution {
public:
vector<int> maximumBeauty(vector<vector<int>>& items, vector<int>& queries) {
// 按价格排序
sort(items.begin(), items.end());
// 去除相同价格的物品,只保留美丽值最大的
vector<vector<int>> processed;
for (auto& item : items) {
if (processed.empty() || processed.back()[0] != item[0]) {
processed.push_back(item);
} else {
processed.back()[1] = max(processed.back()[1], item[1]);
}
}
// 计算前缀最大美丽值
for (int i = 1; i < processed.size(); i++) {
processed[i][1] = max(processed[i][1], processed[i-1][1]);
}
vector<int> result;
for (int query : queries) {
// 二分查找最后一个价格 <= query 的物品
int left = 0, right = processed.size() - 1, ans = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (processed[mid][0] <= query) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
result.push_back(ans == -1 ? 0 : processed[ans][1]);
}
return result;
}
};
class Solution:
def maximumBeauty(self, items: List[List[int]], queries: List[int]) -> List[int]:
# 按价格排序
items.sort()
# 去除相同价格的物品,只保留美丽值最大的
processed = []
for price, beauty in items:
if not processed or processed[-1][0] != price:
processed.append([price, beauty])
else:
processed[-1][1] = max(processed[-1][1], beauty)
# 计算前缀最大美丽值
for i in range(1, len(processed)):
processed[i][1] = max(processed[i][1], processed[i-1][1])
result = []
for query in queries:
# 二分查找最后一个价格 <= query 的物品
left, right = 0, len(processed) - 1
ans = -1
while left <= right:
mid = (left + right) // 2
if processed[mid][0] <= query:
ans = mid
left = mid + 1
else:
right = mid - 1
result.append(0 if ans == -1 else processed[ans][1])
return result
public class Solution {
public int[] MaximumBeauty(int[][] items, int[] queries) {
// 按价格排序
Array.Sort(items, (a, b) => a[0].CompareTo(b[0]));
// 去除相同价格的物品,只保留美丽值最大的
List<int[]> processed = new List<int[]>();
foreach (var item in items) {
if (processed.Count == 0 || processed[processed.Count - 1][0] != item[0]) {
processed.Add(new int[] { item[0], item[1] });
} else {
processed[processed.Count - 1][1] = Math.Max(processed[processed.Count - 1][1], item[1]);
}
}
// 计算前缀最大美丽值
for (int i = 1; i < processed.Count; i++) {
processed[i][1] = Math.Max(processed[i][1], processed[i - 1][1]);
}
int[] result = new int[queries.Length];
for (int i = 0; i < queries.Length; i++) {
// 二分查找最后一个价格 <= query 的物品
int left = 0, right = processed.Count - 1, ans = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (processed[mid][0] <= queries[i]) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
result[i] = ans == -1 ? 0 : processed[ans][1];
}
return result;
}
}
var maximumBeauty = function(items, queries) {
items.sort((a, b) => a[0] - b[0]);
let maxBeauty = 0;
for (let i = 0; i < items.length; i++) {
maxBeauty = Math.max(maxBeauty, items[i][1]);
items[i][1] = maxBeauty;
}
const result = [];
for (let query of queries) {
let left = 0, right = items.length - 1;
let answer = 0;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (items[mid][0] <= query) {
answer = items[mid][1];
left = mid + 1;
} else {
right = mid - 1;
}
}
result.push(answer);
}
return result;
};
复杂度分析
| 复杂度类型 | 大小 |
|---|---|
| 时间复杂度 | O(n log n + m log n),其中 n 是 items 的长度,m 是 queries 的长度。排序需要 O(n log n),每个查询的二分查找需要 O(log n) |
| 空间复杂度 | O(n),用于存储处理后的物品数组 |
相关题目
. Closest Room (Hard)
. Maximum Sum Queries (Hard)