uva 11997 K Smallest Sum(多路并归+优先队列)

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



你可能感兴趣的:(优先队列,多路并归)