序列
原题链接:序列
题目大意
给你(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;
}