UVA 11997 K Smallest Sums 优先队列+归并 STL

  题目链接: UVA......

  题目描述: 有K组数, 每组数有K个, 所以每组数各选一个加和有k^k种情况, 要求输出其中的最小的前k种, 从小到大输出

  解题思路: 首先对于两个数组取前K个, 构造二元组(s, b) 其中s = Aa + Bb , a, b 为下标。 为什么不用三元组(s,a,b)呢, 因为二元组完全可以表示三元组, 下一个元素就是s+B[i+1]-B[i] .

      

        我们需要把这k^2个和组织成如下k个有序表.(A, B是有序的哦)

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

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

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

          再说这种构造方法为什么可行, 这种构造方法保证了当最小的出队之后, 下一个推进的一定是最优的, 因为如上图所示, 对于每组数, 相邻两项相差的都是B[i]-B[i-1], 而推出的机制又保证了数值的最小, 所以与推出的数据相差最小的一定是推出数据的后一个元素

        最后再两两归并算出最终结果, 复杂度是n^2logn(排序)

  代码:

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

//priority_queue<int> heap;
//priority_queue<int, vector<int>, greater<int>() > mm;
struct Item {
    int s, b; // s = Aa + Bb 所以用下标b和sum二元组表示 k个数组中的元素
    bool operator < ( const Item & r ) const {
        return s > r.s;
    }
};
const int MAXN = 800;
int a[MAXN][MAXN];
int k;

void merge( int * A, int * B, int * C ) { // A,B是待处理数组,C是结果数组
    int cnt = 0;
    priority_queue<Item> pq;
    for( int i = 0; i < k; i++ ) {
        pq.push((Item){A[i]+B[0], 0}); // 将新建的k个数组中的第一个推进队列, 最小的肯定在其中
    }
    while(!pq.empty()) {
        Item t = pq.top(); // 通过最小堆得到最小值
        pq.pop();
        C[cnt++] = t.s;
        if( t.b + 1 < k ) {
            pq.push((Item){t.s + (B[t.b+1]-B[t.b]), t.b + 1}); // 推进弹出队列的数组中下一个元素(为什么可行题解中会解释)
        }
        if( cnt == k ) break;
    }
}

int main() {
    while( ~scanf( "%d", &k ) ) {
        memset(a, 0, sizeof(a));
        for( int i = 0; i < k; i++ ) {
            for( int j = 0; j < k; j++ ) {
                scanf( "%d", &a[i][j] );
            }
            sort(a[i], a[i] + k);
        }
        for( int i = 1; i <= k-1; i++ ) {
            merge(a[0], a[i], a[0]);
        }
        for( int i = 0; i < k; i++ ) {
            if( i == 0 ) {
                printf( "%d", a[0][i]);
            }
            else {
                printf( " %d", a[0][i]);
            }
        }
        printf( "\n" );
    }
    return 0;
}

  思考: 一开始我用了错误的思路, 还以为很对......就是找出每组最小的数, 再找出次小的数推入队列, 这样不能保证是最小的, 因为如果有两个分别大一, 而有一个大三,这个算法会选择大三的......所以这道题难点就是如何构造这个优先队列, 一定要能够保证推出的数的下一个数是最小的。看来以后做题不能直接想当然的上手去写, 写之前多多思考, 仔细想想自己的算法是否能够满足自己想达到的目的, 再去动笔写

时间: 2024-12-22 19:09:01

UVA 11997 K Smallest Sums 优先队列+归并 STL的相关文章

UVa 11997 K Smallest Sums 优先队列&amp;&amp;打有序表&amp;&amp;归并

UVA - 11997 K Smallest Sums Time Limit: 1000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status You're given k arrays, each array has k integers. There are k^k ways to pick exactly one element in each array and calculate the sum o

UVA 11997 K Smallest Sums 优先队列 多路合并

vjudge 上题目链接:UVA 11997 题意很简单,就是从 k 个数组(每个数组均包含 k 个正整数)中各取出一个整数相加(所以可以得到 kk 个结果),输出前 k 小的和. 这时训练指南上的一道题,这道题的简化版其实在 15 年的广东省省赛出现过,当时是以送分题的形式出现的,可我还是没能做出来,归根到底还是看书不够,接触的题型不够多. *************************************************************大白书上的讲解开始*******

uva 11997 K Smallest Sums 优先队列处理多路归并问题

题意:K个数组每组K个值,每次从一组中选一个,共K^k种,问前K个小的. 思路:优先队列处理多路归并,每个状态含有K个元素.详见刘汝佳算法指南. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<stack> 5 #include<queue> 6 #include<vector> 7 #include<map> 8 #includ

uva 11997 K smallest sums (优先队列 多路归并)

算法入门经典 训练指南 p189 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; struct Item { int s,b; Item(int s,int b) :s(s),b(b) {} bool operator < (const Item&rhs) co

【优先队列之多路合并】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 思路 二路归并构建二维平面表:

11997 - K Smallest Sums(优先队列)

11997 - K Smallest Sums You’re given k arrays, each array has k integers. There are kk ways to pick exactly one element in eacharray and calculate the sum of the integers. Your task is to find the k smallest sums among them.InputThere will be several

(DS 《算法竞赛入门经典》)UVA 11997 K Smallest Sums

题目大意:有k个数组,每个数组选取一个数,组成k^k个数.在这k^k个数中选择最小的前k个数 解题思路: 1.如果只有k个数组,那么最后得到的最小的前k个数应该可以由前两个数组得到的最小k个数与第三个数组 按规则运算后得到. 2.如果每个数组只有3个数.那么前两个数组(a:(a0,a1,a2)    b:(b0,b1,b2,a与b数组都已经有序)运算后有的结果矩阵如下: a0+b0,a0+b1,a0+b2 a1+b0,a1+b1,a1+b2 a2+b0,a2+b1,a2+b2 在这个矩阵中,a0

UVA 11997 K Smallest Sums (多路归并)

从包含k个整数的k个数组中各选一个求和,在所有的和中选最小的k个值. 思路是多路归并,对于两个长度为k的有序表按一定顺序选两个数字组成和,(B表已经有序)会形成n个有序表 A1+B1<=A1+B2 A2+B1<=A2+B2 ... An+B1<=An+B2 在学习的归并排序的时候是把两个有序的表合并成一个,每次比较只在两个元素之间进行,所以只需要用>比较, 而现在需要同时合并n个有序表,优先队列(堆)就派上用场了.类似归并排序用i和j维护有序表当前考虑元素, 合并的时候,每次取出的

优先队列 UVA 11997 K Smallest Sums

题目传送门 题意:训练指南P189 分析:完全参考书上的思路,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小的数字和,当第i行的数组读入时,先push第一个,也就是最小的,然后可以更新成第二个,就是 - B[i]