题意:给出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; }