算法竞赛进阶指南---0x17(二叉堆)序列

题面

在这里插入图片描述

输入样例

1
2 3
1 2 3
2 2 3

输出样例

3 3 4

题解

  1. 模拟样例,直接看图
    在这里插入图片描述
  1. 我们这样的答案最终有 nm 种,但是我们只用关心前n个小的数即可,那么我们就可以逐行归并,就是总共会有m行,每行有n个数,对于前两行,我们会找出 n2 种答案,因为我们只需要前n个数,那么我们就保留答案的前n个数,然后用这n个数继续和第三行合并,最终合并 m-1 次 就会的到最终的结果,也就是最小的n个数
  1. 那么如何合并两行并找出前n个小的数,暴力枚举,O(n2) *(m-1) n和m都是 1e3 ,肯定是超时的,那么我们如何优化呢,看图。 我们先将a数组排序,然后对 n2 种结果分类,可以分成n类,就是图中的每行,我们就可以发现,每行都是从小到大排列的,每行的开头值都是最小的,那么就好办了,我们只需要维护一个小根堆就好(蓝颜色圈),开始先将b1+a1,b2+a1 …bn+a1 放入小根堆,那么堆顶元素就是这n2 种结果最小的,假设b3+a1是堆顶元素,然后将其从堆顶删除,然后将b3+a2放入小根堆,这样n次,就可以找出n2 种答案的前n个值,是O(nlong)
    在这里插入图片描述

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>

using namespace std;
typedef pair<int, int> PII;
const int N = 2010;

int t, n, m;
int a[N], b[N], c[N];

void merge() {
    
    
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    for (int i = 1; i <= n; i++) {
    
    
        heap.push({
    
    a[1] + b[i], 1});
    }
    for (int i = 1; i <= n; i++) {
    
    
        auto x = heap.top();
        heap.pop();
        int value = x.first;
        int index = x.second;
        c[i] = value;
        heap.push({
    
    value - a[index] + a[index + 1], index + 1});
    }
    for (int i = 1; i <= n; i++) {
    
    
        a[i] = c[i];
    }

}

int main() {
    
    

    cin >> t;
    while (t--) {
    
    
        cin >> m >> n;
        for (int i = 1; i <= n; i++) {
    
    
            scanf("%d", &a[i]);
        }

        sort(a + 1, a + 1 + n);

        for (int i = 1; i <= m - 1; i++) {
    
    
            for (int j = 1; j <= n; j++) {
    
    
                scanf("%d", &b[j]);
            }
            merge();
        }

        for (int i = 1; i <= n; i++) {
    
    
            cout << a[i] << " ";
        }
        cout << endl;
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/113829456