洛谷题单指南-二叉堆与树状数组-P1631 序列合并

原题链接:https://www.luogu.com.cn/problem/P1631

题意解读:两个长度为n的单调不降的有序序列,各取一个数相加,计算最小的n个和。

解题思路:

设两个序列为a[N], b[N]

和最小的为a[1] + b[1]

和第二小的可能有两种:a[1] + b[2]或者a[2]+b[1]

假设第二小为a[1] + b[2],那么和第三小的将在a[2] + b[2],a[1] + b[3],a[2] + b[1]中选

假设第三小为a[2] + b[1],那么和第四小的将在a[2] + b[2],a[1] + b[3], a[2] + b[2], a[3] + b[1]中选

注意a[2] + b[2]出现两次,应该去重,只在a[2] + b[2],a[1] + b[3],  a[3] + b[1]中选

因此,此题算法逻辑如下:

1、设ai=1,bi=1,sum=a[ai]+b[bi],将{ai, bi, sum}存入优先队列,sum小的在堆顶

2、取堆顶元素,输出sum

3、基于堆顶元素的ai,bi,扩展出{ai, bi + 1, a[ai] + b[bi + 1]},{ai + 1, bi, a[ai + 1], b[bi]},加入优先队列

注意:加入优先队列前要对已经加入过的下标进行去重,可以借助map<int, map<int, bool>>来对两个下标进行去重。

4、重复步骤2,一共n次

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int n;
int a[N], b[N];
map<int, map<int, bool>> flag;

struct Node
{
    int ai, bi, sum;
    bool operator < (const Node &node) const &
    {
        return sum > node.sum;
    }
};

priority_queue<Node> q;

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];
    q.push({1, 1, a[1] + b[1]});
    for(int i = 1; i <= n; i++)
    {
        Node node = q.top(); q.pop();
        cout << node.sum << " ";
        if(!flag[node.ai + 1][node.bi])
        {
            q.push({node.ai + 1, node.bi, a[node.ai + 1] + b[node.bi]});
            flag[node.ai + 1][node.bi] = true;
        }
            
        if(!flag[node.ai][node.bi + 1])
        {
            q.push({node.ai, node.bi + 1, a[node.ai] + b[node.bi + 1]});
            flag[node.ai][node.bi + 1] = true;
        }
    }
    return 0;
}

 

posted @ 2024-11-12 09:07  五月江城  阅读(6)  评论(0编辑  收藏  举报