题目大意:
多组数据,每组给出 M M 个序列,每个序列有个元素,从每个序列中任选一个数求和,显然有 NM N M 种和,求最小的 N N 个序列和分别是多少。
分析:
用优先队列动态维护前
N
N
小的序列和,
因为对优先队列的运用不熟练,所以写的比较生疏,
大致就是,设我们已经做完了前个序列,长度为
i
i
的序列的前的序列和为数组
f[]
f
[
]
,即
f[1
f
[
1
~
N]
N
]
,那么显然,
现将
f[]
f
[
]
升序排列,
对于第
i+1
i
+
1
个序列,
我们序列长度为
i+1
i
+
1
的序列和最小值,显然就是
f[1]+a[i+1][1]
f
[
1
]
+
a
[
i
+
1
]
[
1
]
,
那么次小值,可能就是
f[2]+a[i+1][1],f[1]+a[i+1][2]
f
[
2
]
+
a
[
i
+
1
]
[
1
]
,
f
[
1
]
+
a
[
i
+
1
]
[
2
]
,我们一直这样找然后用优先队列维护一个最小堆即可,
有一个优化,对于
f[1]+a[2]跟f[2]+a[1]
f
[
1
]
+
a
[
2
]
跟
f
[
2
]
+
a
[
1
]
,显然都能到
f[2]+a[2]
f
[
2
]
+
a
[
2
]
,这里的重复可以优化掉。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define N 2005
using namespace std;
struct Node
{
int id1, id2, num1, num2;
bool flag;
friend bool operator < (Node aa, Node bb)
{
return (aa.num1 + aa.num2) > (bb.num1 + bb.num2);
}
Node(){};
Node(int a, int b, int c, int d, bool e)
{
id1 = a, id2 = b, num1 = c, num2 = d, flag = e;
}
}orz;
priority_queue <Node> Q;
int a[N], f[N], n, m;
void Merge()
{
while (Q.size()) Q.pop();
int C[N], cnt = 0;
Q.push(Node(1, 1, f[1], a[1], 0));
while (Q.size() && cnt < m)
{
orz = Q.top();
Q.pop();
C[++cnt] = orz.num1 + orz.num2;
if (orz.id1 < m && !orz.flag)
Q.push(Node(orz.id1 + 1, orz.id2, f[orz.id1 + 1], a[orz.id2], 0));
if (orz.id2 < m)
Q.push(Node(orz.id1, orz.id2 + 1, f[orz.id1], a[orz.id2 + 1], 1));
}
for (int i = 1; i <= m; i++) f[i] = C[i];
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) scanf("%d", &f[i]);
sort(f + 1, f + m + 1);
for (int i = 2; i <= n; i++)
{
for (int j = 1; j <= m; j++) scanf("%d", &a[j]);
sort(a + 1, a + m + 1);
Merge();
}
for (int i = 1; i <= m; i++) printf("%d ", f[i]);
printf("\n");
}
return 0;
}