uva 11997 K Smallest Sums 优先队列

题目:https://uva.onlinejudge.org/external/119/11997.pdf



对于两组数a[n],b[n],每组长度为n,现在要得到a[i]+b[j] (1<=i,j<=n)的和   中前n小的值。

先对b[n]排序

方法是利用b[1] + x<b[2]+x<...<b[n]+x;

构造一个优先队列(小的优先)

先放入n个值 (a[1]+b[1]、a[2]+b[1]、a[3]+b[1]、......、a[n]+b[1]);

弄出n个指针p[i],分别表示队列里a[i]加上的是b[p[i] ];

每次取出队头,删去,然后补上一个新的元素。

比如取出 a[i]+b[p[i] ]

那么得到一个答案a[i]+b[p[i] ],然后放入a[i]+b[p[i] +1];

进行n次,那么可以得到前n小的数


变式:

对于两组数a[n],b[m],现在要得到a[i]+b[j] (1<=i<=n,1<=j<=m)的和   中前k小的值。

先对b[m]排序

方法是利用b[1] + x<b[2]+x<...b[m]+x;

构造一个优先队列(小的优先)

先放入n个值 (a[1]+b[1]、a[2]+b[1]、a[3]+b[1]、......、a[n]+b[1]);

弄出n个指针p[i],分别表示队列里a[i]加上的是b[p[i] ];

每次取出队头,删去,然后补上一个新的元素。

比如取出 a[i]+b[p[i] ]

那么得到一个答案a[i]+b[p[i] ],然后放入a[i]+b[p[i] +1];

进行k次,那么可以得到前k小的数


变式:

有n组数,每组数取一个数相加  得到一个结果,要求前k小的结果分别是多少。

对于每两组数,用到的一定是这两组数中(分别取1个数,他们的 和中)最小的k个数,

故将这两组变成了一组,减少了组数,依此类推,直到最后剩下一组(长度为k)


const int INF =0x3f3f3f3f;
const int maxn= 760   ;
//const int maxm=    ;
//by yskysker123

int n,a[maxn][maxn];
int p[maxn];
struct Node
{
    int num,index;
    Node(){}
    Node(int num,int index):num(num),index(index){}
};
struct cmp
{
    bool operator()(Node x,Node y   )
    {
        return x.num>y.num;
    }
};
void merge(int le,int ri)
{
    priority_queue<Node,vector<Node > ,cmp> q;
    for(int i=1;i<=n;i++)
    {
        p[i]=1;
        q.push(  Node( a[le][i]+a[ri][1],i)  );
    }
    for(int ii=1;ii<= n ;ii++)
    {
        Node tmp=q.top();q.pop();
        int index=tmp.index;
        int num=tmp.num;
         a[le][ii]=num ;

        p[index]++;
        if(p[index]<=n  )
        q.push(Node (num-a[ri][p[index]-1   ]+a[ri][p[index] ]  ,index   )   );


    }

}
int main()
{
    int x;
    while(~scanf("%d",&n))  //有一种错误叫做忘写~或!=EOF,这种错误常常是由于移动代码时造成的,通常还会在事先
    {                       //表现为 () 内有;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&a[i][j]);
            }
            sort(a[i]+1,a[i]+1+n);
        }
        for(int i=2;i<=n;i++)
        {
            merge(1,i );
        }
        for(int i=1;i<n ;i++)
            printf("%d ",a[1][i]);
        printf("%d\n",a[1][n]);


    }



    return 0;
}


#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI 3.1415926535897932384626
#define eps 1e-10
#define sqr(x) ((x)*(x))
#define FOR0(i,n)  for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n)  for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n)  for(int i=(n) ;i>=0 ;i--)
#define  lson   num<<1,le,mid
#define rson    num<<1|1,mid+1,ri
#define MID   int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk    make_pair
#define _f     first
#define _s     second

using namespace std;
//const int INF=    ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值