📌  相关文章
📜  给定两个数组的最小总和(1)

📅  最后修改于: 2023-12-03 15:41:16.337000             🧑  作者: Mango

给定两个数组的最小总和

给定两个长度相同的数组 nums1nums2,将它们视作下标从 0 开始的列表,用一个下标序列表示。任何一个子序列(顺序不变)可以通过它们的下标序列进行表示。例如,[1,3,2] 表示为对应数组中下标为 1、3 和 2 的元素。

求出同时从nums1nums2 中选出至少 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],然后依次枚举 ij,并把它们的和 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;
    }
};