UVA11997 - K Smallest Sums 优先队列,多路归并

题目要求是从k个数组中各选一个数组成的和中最小的k个。

用到的是多路归并算法,详见汝佳神白书。先看看两个数组的情况,就会有如下几个表单

表1:A1+B1<=A1+B2<=......<=A1+Bk

表2: A2+B1<=A2+B2<=......<=A2+Bk

...................................

表k:Ak+B1<=AK+B2<=......<=Ak+Bk

     可以很容易的知道,这里有K个表单(因为数组有K个元素),并且每个表单的最小值都是第一个(因为两个数组在之前应该要是排好序的),那我们就可以吧每个表单的首个元素加到队列里面。

    然后根据汝佳神的公式:我们可以用二元组(s,b)来表示一个元素即s=Aa+Bb;如果我们需要元素(s,b)在表a的下一个元素(s',b+1).只需要计算s'=s+B[b+1]-B[b];根据这个公式,我们可以有队列中的第一个元素推出该元素所在表单中的第二个元素,如已知优先队列中第一个元素为A1+B1,即这是当前最小值,然后根据公式可以推出A1+B2,并把它加入到优先队列中,然后在取出优先队列中的第一个元素。。。。依次类推,每次取得都是仅次于上次的最小值,取K次可得结果。。。

以上是两个数组的,面对K个数组的情况,只需两两合并就行,详见代码

代码如下:

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

struct My
{
    int s,b;
    My(int s,int b):s(s),b(b) {}
    bool operator>(const My &m)const
    {
        return s>m.s;
    }
};

void mymerge(int A[],int B[],int C[],int n)
{
    priority_queue<My>pq;
    sort(A,A+n);
    sort(B,B+n);
    for(int i=0; i<n; i++)
        pq.push(My(A[i]+B[0],0));
    for(int i=0; i<n; i++)
    {
        My item=pq.top();
        pq.pop();
        C[i]=item.s;
        int b=item.b;
        if(b+1<n)
            pq.push(My(item.s-B[b]+B[b+1],b+1));
    }
}
int cun[800][800];
int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++) cin>>cun[i][j];
            sort(cun[i],cun[i]+n);
        }
        for(int i=1; i<n; i++)
            mymerge(cun[0],cun[i],cun[0],n);//两两合并数组,把每一次的最小值都放到cun[0];
        for(int i=0; i<n-1; i++)
            cout<<cun[0][i]<<" ";
        cout<<cun[0][n-1]<<endl;

    }
    return 0;
}
   其实一开始并没有想到这么做,一开始我的想法是: 找出k个数组中每个数组的最小值 这样得到的肯定是最小的, 那么再找其余k-1个最小值。我保存了 每个数组中的值减去那个最小值得到的结果, 这样就想 每次换一个数 尽量换小的。并且每次换了之后就把这次换的值和上次换的值相加,因为可能会有几个数组同时交换,但是当差值为零的时候比较麻烦,一直WA,,最后终于放弃了。。待以后冷静点了再来看看。。。。。这个思路我认为是没错的,可供借鉴


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值