【UVA 11997 K Smallest Sums】优先级队列

简介: 来自《训练指南》优先级队列的例题。 题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18702 题意:给定k个整数数组,各包含k个元素。

来自《训练指南》优先级队列的例题。

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

题意:给定k个整数数组,各包含k个元素。在每个数组中取一个元素加起来,可以得到kk个和,求这些和中最小的k个值(不去重)。

数据范围:k [2, 750]

思路:暴力枚举k^k不可取。

“先来看问题的简化版:给出两个长度为k的数组A和B,分别在A和B中任取一个数并相加,可以得到k^2个和,求这些和中最小的k个。”

首先把A, B从小到大排序。由于只求最小的前k个值,我们可以先假定B[0]与A的k个元素分别相加得到的元素集合S就是最小的k个和,然后每次取出S的最小值Smin保存到结果数组R中,并把Smin减去B[i]、加上B[i+1]后得到的值再放回集合S中,保持S的大小总为k。这样经过k轮取出-放回操作后,R中的k个元素就是结果。我们把这一操作过程叫作A,B的合并。合并一次的复杂度为O(klogk)

S需要k次插入和删除,每次只取最小值,因此数据结构选用完全二叉堆实现的优先级队列很合适。

现在由两个数组A,B扩展到k个数组A1~Ak,只需把A1看作A, 依次处理 i: 2~k,每次把Ai合并到A1中,这样经过k次两两合并后,A1中的k个数就是结果。总的复杂度O(k2logk)

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <queue>
 4 using namespace std;
 5 const int MAX_K = 760;
 6 
 7 struct Elem{
 8     int sum, b;
 9     Elem(int s, int b):sum(s), b(b){}
10     Elem& operator = (const Elem& e){
11         sum = e.sum;
12         b = e.b;
13         return *this;
14     }
15     bool operator < (const Elem& e) const{
16         return sum > e.sum; //小顶堆 
17     }
18 };
19 
20 void merge(int* A, int* B, int n){
21     priority_queue<Elem> pq;
22     for(int i=0; i<n; i++){
23         pq.push(Elem(A[i] + B[0], 0));
24     }
25     for(int i=0; i<n; i++){
26         Elem e = pq.top();
27         A[i] = e.sum;
28         pq.pop();
29         e.sum = e.sum - B[e.b] + B[e.b+1];
30         e.b = e.b + 1;
31         pq.push(e);
32     }
33 //    for(int i=0; i<n; i++){
34 //        printf("%d ", A[i]);
35 //    }
36 //    printf("\n");
37 }
38 
39 int k;
40 int a[MAX_K], b[MAX_K];
41 
42 int main()
43 {
44     freopen("11997.txt", "r", stdin);
45     while(~scanf("%d", &k)){
46         for(int i=0; i<k; i++){
47             scanf("%d", &a[i]);
48         }
49         sort(a, a+k);
50         for(int i=1; i<k; i++){
51             for(int j=0; j<k; j++){
52                 scanf("%d", &b[j]);
53             }
54             sort(b, b+k);
55             merge(a, b, k);
56         }
57         printf("%d", a[0]);
58         for(int i=1; i<k; i++){
59             printf(" %d", a[i]);
60         }
61         printf("\n");
62     }
63     return 0;
64 }
目录
相关文章
uva1203 - Argus (排序、优先级队列)
uva1203 - Argus (排序、优先级队列)
55 0
UVa872 - Ordering(拓扑排序)
UVa872 - Ordering(拓扑排序)
61 0
|
算法
The kth great number(小根堆思想,模板题)
The kth great number(小根堆思想,模板题)
52 0
The kth great number(小根堆思想,模板题)
|
开发框架 .NET
poj 3468 A Simple Problem with Integers线段树区间修改
题目意思很简单,有N个数,Q个操作, Q l r 表示查询从l到r 的和,C l r v 表示将从l到r 的值加上v,明显的线段树,不知道线段树的人肯定暴力,肯定超时,哈哈!!
34 0
|
C++
poj 2182 Lost Cows(树状数组)
FJ有n头牛,现在它们排成一排,每一头牛都有一个不同的编号(1-n),现在知道从第二头牛开始每头牛左边比自己编号小的牛的数目,让你确定每头牛的编号。
43 0
UVa10596 - Morning Walk(并查集)
UVa10596 - Morning Walk(并查集)
61 0
uva133 The Dole Queue
uva133 The Dole Queue
65 0
UVA699 下落的树叶 The Falling Leaves
UVA699 下落的树叶 The Falling Leaves
UVA699 下落的树叶 The Falling Leaves
【1094】The Largest Generation (树DFS-水题)
基础题。 这类题也是直接DFS,不是像上次的【1079】&【1090】供应树DFS一样是到了叶子结点就进行“更新/结算”类型,这题直接利用DFS统计每层的结点个数即可。
100 0