AcWing 序列
Description
给定m个序列,每个包含n个非负整数。
现在我们可以从每个序列中选择一个数字以形成具有m个整数的序列。
很明显,我们一共可以得到n^m个这种序列, 然后我们可以计算每个序列中的数字之和,并得到n^m个值。 现在请你求出这些序列和之中最小的n个值。
Input
第一行输入一个整数T,代表输入中包含测试用例的数量。
接下来输入T组测试用例。
对于每组测试用例,第一行输入两个整数m和n。
接下在m行输入m个整数序列,数列中的整数均不超过10000。
Output
对于每组测试用例,均以递增顺序输出最小的n个序列和,数值之间用空格隔开。
每组输出占一行。
Data Size
- 0<m≤1000,
0<n≤2000
Sample Input
1 2 3 1 2 3 2 2 3
Sample Output
3 3 4
题解:
- 二叉堆。
- 妙题,妙不可言。
- lyd老师讲得太好了,我就直接当一回搬运工:D!
- 先来考虑M = 2的简化问题,即从2个序列中任取一个数相加构成的N ^ 2个和中求出前N小的和。设这两个序列为A和B,把它们分别排序。
- 可以发现,最小和一定是A[1] + B[1],次小和是min(A[1] + B[2], A[2] + B[1])。假设次小和是A[2] + B[1],那么第3小和就是A[1] + B[2],A[2] + B[2], A[3] + B[1]的三者之一。也就是说,当确定A[i] + B[j]为第k小和时,A[i + 1] + B[j]和A[i] + B[j + 1],就加入了第k + 1小和的备选答案中。
- 需要注意的是,A[1] + B[2]和A[2] + B[1]都可以产生A[2] + B[2]这个备选答案。为了避免重复,我们可以规定:如果把j + 1加入备选答案,那么以后就只能再增加j,不能增加i。
- 所以,我们建立一个小根堆,堆中每个节点存储一个三元组(i, j, last),其中last表示上一次移动的指针是不是j。起初,堆中只有(1, 1, false)。
- 那么每一步取出堆顶(i, j, last),然后把(i, j + 1, true)插入堆,如果last为false,再把(i + 1, j, false)插入堆。重复N次,每次取出堆顶一起构成前N小和。
- 回到本题,根据数学归纳法,先求出前2个序列的前N小和作为新的序列,然后再将新序列与后面的序列合并,最终得到答案。
- 总结:有时思考问题要将问题缩放,即考虑最小规模的数据,看看能不能类比,逐步推出问题规模的数据。(像这一题,有一种逐渐浓缩、缩点的味道)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#define N 1005
#define M 2005
using namespace std;
struct Node
{
int i, j, val, las;
friend bool operator < (Node x, Node y) {
return x.val > y.val;
}
};
int T, n, m;
int a[M], b[M], c[M];
int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
return x *= f;
}
int main()
{
cin >> T;
while(T--)
{
n = read(), m = read();
for(int i = 1; i <= m; i++) a[i] = read();
sort(a + 1, a + 1 + m);
for(int i = 2; i <= n; i++)
{
for(int j = 1; j <= m; j++) b[j] = read();
sort(a + 1, a + 1 + m), sort(b + 1, b + 1 + m);
priority_queue<Node> que;
que.push((Node){1, 1, a[1] + b[1], 0});
c[1] = a[1] + b[1];
for(int j = 2; j <= m; j++)
{
Node now = que.top(); que.pop();
que.push((Node){now.i, now.j + 1, a[now.i] + b[now.j + 1], 1});
if(!now.las) que.push((Node){now.i + 1, now.j, a[now.i + 1] + b[now.j], 0});
c[j] = que.top().val;
}
for(int j = 1; j <= m; j++) a[j] = c[j];
}
for(int j = 1; j <= m; j++) printf("%d ", a[j]);
printf("\n");
}
return 0;
}