题面
输入样例
1
2 3
1 2 3
2 2 3
输出样例
3 3 4
题解
- 模拟样例,直接看图
- 我们这样的答案最终有 nm 种,但是我们只用关心前n个小的数即可,那么我们就可以逐行归并,就是总共会有m行,每行有n个数,对于前两行,我们会找出 n2 种答案,因为我们只需要前n个数,那么我们就保留答案的前n个数,然后用这n个数继续和第三行合并,最终合并 m-1 次 就会的到最终的结果,也就是最小的n个数
- 那么如何合并两行并找出前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;
}