-
理解题目要求:
- 给定
m
个数字序列,每个序列有n
个非负整数,我们需要从每个序列中选取一个数字构成新的数字序列。 - 我们可以从每个序列中选一个数字,生成一个新的数字序列,总共可以生成
n^m
个序列。 - 对于每个新的数字序列,计算它们的和,并找出最小的
n
个和。
- 给定
-
思路:
- 排序与合并:我们首先将第一个序列的数字排序,然后将每个后续序列的数字与当前的和数组合并。
- 使用最大堆:为了得到最小的
n
个和,我们使用一个最大堆(priority_queue<int>
)。堆中的大小始终保持为n
,这样堆顶始终存储当前最小的和。 - 合并新序列:对于每一个新的序列,我们与当前的
n
个最小和进行合并。每当找到一个更小的和时,就将堆顶的元素弹出并加入新的和。 - 反向存储最小和:每次合并后,我们将堆中的元素按从小到大的顺序存储回数组
a
中。
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
// 定义一个优先队列(大根堆),用来存储当前的最小n个和
priority_queue<int> que;
// 定义两个数组,a 用来存储当前的最小n个和,b 用作中间数组
int a[2005];
int b[2005];
int main() {
int T; // 测试用例数量
cin >> T; // 读取测试用例数量
int m, n; // m表示序列的个数,n表示每个序列的长度
while (T--) { // 逐个处理测试用例
cin >> m >> n; // 读取每组测试用例的 m 和 n
// 读取第一个序列并排序,作为最初的和数组
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]); // 读取第一个序列的数字
}
sort(a, a + n); // 将数组 a 排序,从小到大
// 从第二个序列到第 m 个序列,依次处理
for (int i = 1; i < m; i++) {
// 读取第 i 个序列,并将其与当前最小的和数组 a 合并
for (int j = 0; j < n; j++) {
scanf("%d", &b[j]); // 读取当前序列
que.push(a[0] + b[j]); // 将当前序列的数字与 a[0] 的和加入堆中
}
// 更新堆,保证堆中只保留最小的 n 个和
for (int j = 1; j < n; j++) { // 对接下来的每个数字
for (int k = 0; k < n; k++) { // 遍历 b 数组
if (a[j] + b[k] < que.top()) { // 如果当前和比堆顶的最大值小
que.pop(); // 弹出堆顶最大值
que.push(a[j] + b[k]); // 将当前和放入堆中
}
}
}
// 将堆中的最小 n 个和存回 a 数组
for (int i = 0; i < n; i++) {
a[n - i - 1] = que.top(); // 将堆顶的值放回 a 数组(按从小到大的顺序)
que.pop(); // 弹出堆顶元素
}
}
// 输出结果,打印最小的 n 个和
for (int i = 0; i < n - 1; i++)
printf("%d ", a[i]);
printf("%d\n", a[n - 1]); // 输出最后一个数,不需要空格
}
return 0;
}