【优先队列之多路合并】UVA - 11997 K Smallest Sums

Source : UVA - 11997 K Smallest Sums

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18702

题意

有k个整数数组,各包含k个元素,从每个数组中选取一个元素加起来,可以得到k^k个和,求这些和中最小的k个值。

示例

Sample Input
3
1 8 5
9 2 5
10 7 6
2
1 1
1 2

Sample Output
9 10 12
2 2


思路

二路归并构建二维平面表:

1A1+B1<=A1+B2<=A1+B32A2+B1<=A2+B2<=A2+B3nAn+B1<=An+B2<=An+B3


首先将第一列入队,第一列的最小值即为全局的最小值;接下来定位到最小值的那一行,跳过这一个元素,把下一个元素入队,始终保持priority_queue中有n个元素,队首元素即为全局下一小的元素和,记录即可。

多路归并就要构建三位立体表:想象下

k×k×k

逐层二路合并即可。

★ 参考刘汝佳《算法竞赛入门经典训练指南》page 190


参考代码

#include<bits/stdc++.h>
using namespace std;

const int _max = 750 + 10;
int n,A[_max],B[_max];
struct Item{
  int s,b;//(s,b) s = Aa + Bb
  Item (){}
  Item(int s,int b):s(s) , b(b){}//构造函数
  bool operator < (const Item& a) const{//定义优先级
    return s > a.s;
  }
};
priority_queue<Item>pq;
Item item;

void merge(int A[],int B[],int C[]){//二路归并,前n个最小和存于C[]
  while(!pq.empty()) pq.pop();
  for(int i = 0; i < n; ++ i){
    item = Item(A[i] + B[0],0);
    pq.push(item); //第一列元素入队
  }
  for(int i = 0; i < n; ++ i){//O(nlogn)
    item = pq.top();pq.pop();
    C[i] = item.s;//第一列最小的一定是全局最小,跳过它读取同一行的下一个元素入队,循环n次
    int b = item.b;
    if(b + 1 < n) pq.push(Item(item.s - B[b] + B[b+1],b + 1));
  }
}

int main(){
 #ifndef ONLINE_JUDGE
 freopen("input.txt","r",stdin);
 #endif // ONLINE_JUDGE
 while(scanf("%d",&n) == 1){
    for(int i = 0; i < n; ++ i) //先读一路数据
       scanf("%d",A + i);
    sort(A , A + n);
    for(int i = 1; i < n; ++ i){ //以二路为基础做多路归并
        for(int j = 0; j < n; ++ j) scanf("%d",B+j);
        sort(B,B+n);
        merge(A,B,A);//A[]元素只用做初始化,所以在合并过程可以直接把结果存储到A中
    }
    printf("%d",A[0]);//输出格式
    for(int i = 1; i < n; ++ i) printf(" %d",A[i]);
    printf("\n");
 }
 return 0;
}
  • 加粗 Ctrl + B
  • 斜体 Ctrl + I
  • 引用 Ctrl + Q
  • 插入链接 Ctrl + L
  • 插入代码 Ctrl + K
  • 插入图片 Ctrl + G
  • 提升标题 Ctrl + H
  • 有序列表 Ctrl + O
  • 无序列表 Ctrl + U
  • 横线 Ctrl + R
  • 撤销 Ctrl + Z
  • 重做 Ctrl + Y
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值