「二叉堆」序列

序列

原题链接:序列

题目大意

给你\(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;
}
posted @ 2019-09-17 20:34  Nicoppa  阅读(148)  评论(0编辑  收藏  举报