📅  最后修改于: 2023-12-03 15:41:16.337000             🧑  作者: Mango
给定两个长度相同的数组 nums1
和 nums2
,将它们视作下标从 0 开始的列表,用一个下标序列表示。任何一个子序列(顺序不变)可以通过它们的下标序列进行表示。例如,[1,3,2]
表示为对应数组中下标为 1、3 和 2 的元素。
求出同时从nums1
和 nums2
中选出至少 k
个下标组成的不重复下标序列,并使得这些下标对应元素的和最小。
输入: nums1 = [1,2], nums2 = [3,4], k = 2 输出: 7 解释: 选出下标为 0 和 1 的元素,它们的和为 1 + 3 = 4。
输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2 输出: 2 解释: 选出下标为 0 和 2 的元素,它们的和为 1 + 2 = 3。
这道题可以使用贪心算法结合大根堆来解决。
我们可以设 sum = nums1[i] + nums2[j]
,然后依次枚举 i
和 j
,并把它们的和 sum
存入大根堆,同时记录一下当前大根堆里面元素的个数,如果这个个数大于 k
,就把堆顶元素弹出,这样就得到了当前下标序列中最小的 k
个和。
为了避免出现下标重复的情况,我们可以用一个 visited
数组记录一下哪些下标被弹出了,然后下次循环的时候在里面判一下就好。
实现的时候,我们可以在堆里面存一个结构体(sum, i, j)
,最终得到了一个包含 k
个元素的序列 [(sum_1, i_1, j_1), (sum_2, i_2, j_2), ..., (sum_k, i_k, j_k)]
,这个序列的和就是我们的答案。
注意:这里要用到大根堆,所以需要手动实现一下,C++ 中可以使用 priority_queue
。
时间复杂度:$O(k \log k)$。需要枚举 $i$ 和 $j$,而每次压入堆中的元素数量最多是 $2k$,所以总共会进行 $2k^2$ 次操作,每次操作需要 $O(\log k)$ 的时间复杂度。
空间复杂度:$O(k)$。堆中最多存储 $k$ 个元素,所以空间复杂度是 $O(k)$。
class Solution {
public:
int minSum(vector<int>& nums1, vector<int>& nums2, int k) {
int n = nums1.size();
priority_queue<tuple<int, int, int>> pq; // 大根堆
vector<bool> visited(n, false); // 记录是否访问过对应下标
pq.push(make_tuple(nums1[0] + nums2[0], 0, 0));
int cnt = 0, ans = 0;
while (cnt < k) {
auto [sum, i, j] = pq.top();
pq.pop();
if (visited[i] || visited[j]) {
continue; // 下标重复
}
visited[i] = visited[j] = true;
ans += sum;
cnt++;
if (i + 1 < n) pq.push(make_tuple(nums1[i + 1] + nums2[j], i + 1, j));
if (j + 1 < n) pq.push(make_tuple(nums1[i] + nums2[j + 1], i, j + 1));
}
return ans;
}
};