UVA 11997 K Smallest Sums (多路归并)

时间:2023-03-08 21:18:40

从包含k个整数的k个数组中各选一个求和,在所有的和中选最小的k个值。

思路是多路归并,对于两个长度为k的有序表按一定顺序选两个数字组成和,(B表已经有序)会形成n个有序表

A1+B1<=A1+B2

A2+B1<=A2+B2

...

An+B1<=An+B2

在学习的归并排序的时候是把两个有序的表合并成一个,每次比较只在两个元素之间进行,所以只需要用>比较,

而现在需要同时合并n个有序表,优先队列(堆)就派上用场了。类似归并排序用i和j维护有序表当前考虑元素,

合并的时候,每次取出的元素要能推出下一个它所在表的下一个元素,所以每个结点维护一个下标信息。

此题稍有变动,要求和的表是k个,一个和有k个元素。

不难发现任意两个元素组成的和一定是它们所在的表合并和结果中前k小的和,否则一定可以替换。

因此每次合并两个,一个是之前累加的结果,另一个是新表。

#include<bits/stdc++.h>
using namespace std;
const int maxk = ;
int a[maxk], b[maxk]; struct Node
{
int s,id;
bool operator < (const Node& y) const {
return s > y.s;
}
}; void Merge(int *A,int *B,int *C,int n)
{
priority_queue<Node> q;
for(int i = ; i < n; i++){
q.push({A[i]+B[],});
}
for(int i = ; i < n; i++){
Node u = q.top(); q.pop();
C[i] = u.s;
int id = u.id;
if(id+ < n) q.push({u.s-B[id]+B[id+],id+});
}
} int main()
{
//freopen("in.txt","r",stdin);
int k;
while(~scanf("%d",&k)){
for(int i = ; i < k; i++) scanf("%d",a+i);
for(int i = ; i < k; i++){
for(int j = ; j < k; j++){
scanf("%d",b+j);
}
sort(b,b+k);
Merge(a,b,a,k);
}
printf("%d",a[]);
for(int i = ; i < k; i++){
printf(" %d",a[i]);
}
putchar('\n');
}
return ;
}