「二叉堆」序列

序列

原题链接:序列

题目大意

给你(m)行,每行(n)个数,让你从(m)行中每行选一个数组成一个长度为(n)的序列,并且一共会组成(n^m)个序列,这些序列中的数求和,让你求出前(n)个最小序列。

题目题解

很经典的题,当作典型模型可以记住

模型:分组,先考虑(m = 2)的情况,我们设第一组为(a_n) 第二组为(b_n) 我们将这两组数单独排序,那么我们可能会得到这样一个错误的写法,就是(a_1 + b_1)是最小 (a_2 + b_2) 是次小,这显然是错误的,为何错误?这里不证

再考虑一下,似乎可以转化成下面这个竖式

(a_1 + b_1)(a_1 + b_2)(a_1 + b_3) ... (a_1 + b_n)

(a_2 + b_1)(a_2 + b_2)(a_2 + b_3) ... (a_2 + b_n)

....

(a_n + b_1)(a_n + b_2)(a_n + b_3) ... (a_n + b_n)

首先每组的最小值一定是第一个,其次是第二个,每次我们选择最小的加入我们的答案

但是这仅是(m = 2)的情况,我们是否可以将所有情况套在这上面?答案是可以的,只要我们将(a_n)定义为我们当前得到的(n)个最大值,我们将(b_n)定义为当前我们读入的(m)行的话,就可以了,维护用小根堆维护

代码如下

//#define fre yes

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

typedef std::pair<int, int> PII;
const int N = 2005;
int a[N], b[N], c[N];

int n, m;

void merge() {
    std::priority_queue<PII, std::vector<PII>, std::greater<PII> > heap;
    for (int i = 1; i <= n; i++) heap.push({a[1] + b[i], 1});
    for (int i = 1; i <= n; i++) {
        PII t = heap.top();
        heap.pop();
        int s = t.first, p = t.second;
        c[i] = s;
        heap.push({s - a[p] + a[p + 1], p + 1});
    }
    
    for (int i = 1; i <= n; i++) {
        a[i] = c[i];
    }
}

int main() {
    static int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &m, &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        } std::sort(a + 1, a + 1 + n);
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j <= n; j++) {
                scanf("%d", &b[j]);
            } merge();
        }
        
        for (int i = 1; i <= n; i++) {
            printf("%d ", a[i]);
        } puts("");
    } return 0;
}
原文地址:https://www.cnblogs.com/Nicoppa/p/11536934.html