题意:给出k个包含k个数的序列,从每个序列中选一个数做和,问前k个最小的和是多少。
本题采用刘汝佳大白书上多路并归的解法。
先考虑2个序列每个序列有k个数的情况。
先假设这两个序列是A和B,且都已经从小到大排好序了。那么先以A序列为基础,把A的每个数先加上B中的最小数得到k个和,此时这里面最小的数肯定是答案中要求的,可以直接把它放到答案里,这个数等于A【1】+B【1】,此时要考虑A【1】+B【2】是否属于答案里。这样把A【1】+B【1】从集合中拿出去,再把A【1】+B【2】加到里面,此时最小的数就是第二个答案,这样以此类推做k次就得到k个答案了。
排序可以用优先队列实现,但是表示某个和是A【1】+B【x】需要设计一个数据结构。
struct node{
int sum;
int ord;
}
sum表示这个和,ord表示是A【a】和B【ord】做的和,其中a实际上是不需要存储的。
对于k个序列,实际上做需要做k次这种多路并归就可以了。每次把A和B并归出的k个数赋给A,此时A表示的是前两个序列的k个最小和,再输入下k个数到B,再并归A,B,以此类推。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int K;
int a[760];
int b[760];
struct node{
int sum;
int ord;
node (){}
node (int sum,int ord):sum(sum),ord(ord){}
bool operator <(const node obj)const{
return sum>obj.sum;
}
};
void Merge(int *A,int *B,int *C){
priority_queue<node> q;
for(int i=1;i<=K;i++){
q.push(node(A[i]+B[1],1));
}
for(int i=1;i<=K;i++){
node tmp=q.top();
q.pop();
C[i]=tmp.sum;
int s=tmp.sum;
int id=tmp.ord;
if(id<K) q.push(node(s+B[id+1]-B[id],id+1));
}
}
int main(){
while(~scanf("%d",&K)){
for(int i=1;i<=K;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+K+1);
for(int i=2;i<=K;i++){
for(int j=1;j<=K;j++){
scanf("%d",&b[j]);
}
sort(b+1,b+K+1);
Merge(a,b,a);
}
for(int i=1;i<=K;i++){
printf("%d",a[i]);
if(i==K) printf("\n");
else printf(" ");
}
}
return 0;
}