题意
题解
若 m = 1 m=1 m=1,使序列升序排序即答案;若 m = 2 m=2 m=2,使序列 A , B A,B A,B 升序排序,若第 k k k 小值为 A [ i ] + B [ j ] A[i]+B[j] A[i]+B[j],则第 k + 1 k+1 k+1 小的备选答案中要加入 A [ i + 1 ] + B [ j ] , A [ i ] + B [ j + 1 ] A[i+1]+B[j],A[i]+B[j+1] A[i+1]+B[j],A[i]+B[j+1](为防止备选答案重复,应该使其产生路径唯一)使用二叉堆维护备选的第 k k k 小值,求解前 n n n 小的和,时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
根据数学归纳法,可以求出前 2 2 2 个序列的前 n n n 小和,将其当做新的序列与第 3 3 3 个序列求解新的前 n n n 小和,最终得到前 m m m 个序列的前 n n n 小和。时间复杂度 O ( n m log n ) O(nm\log n) O(nmlogn)。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct node
{
int x, i, j;
bool operator<(const node &b) const { return x > b.x; }
};
const int maxn = 2005;
int T, M, N, A[maxn], B[maxn], C[maxn];
void solve()
{
priority_queue<node> q;
q.push(node{A[0] + B[0], 0, 0});
for (int i = 0; i < N; ++i)
{
node t = q.top();
q.pop();
C[i] = t.x;
if (t.j + 1 < N)
q.push(node{A[t.i] + B[t.j + 1], t.i, t.j + 1});
if (!t.j && t.i + 1 < N)
q.push(node{A[t.i + 1] + B[t.j], t.i + 1, t.j});
}
memcpy(A, C, sizeof(int) * N);
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &M, &N);
for (int i = 0; i < N; ++i)
scanf("%d", A + i);
sort(A, A + N);
--M;
while (M--)
{
for (int i = 0; i < N; ++i)
scanf("%d", B + i);
sort(B, B + N);
solve();
}
for (int i = 0; i < N; ++i)
printf("%d%c", A[i], i == N - 1 ? '\n' : ' ');
}
return 0;
}