zoukankan      html  css  js  c++  java
  • UVa

    K Smallest Sums

    You're given k arrays, each array has k integers. There are kk ways to pick exactly one element in each array and calculate the sum of the integers. Your task is to find the k smallest sums among them.

    Input

    There will be several test cases. The first line of each case contains an integer k (2<=k<=750). Each of the following k lines contains k positive integers in each array. Each of these integers does not exceed 1,000,000. The input is terminated by end-of-file (EOF). The size of input file does not exceed 5MB.

    Output

    For each test case, print the k smallest sums, in ascending order.

    Sample Input

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

    Output for the Sample Input

    9 10 12

    2 2


    lrj160页优先队列问题。

    但是到这里已经是多路归并和贪心思想了,优先队列只是一个工具。

    简化版问题:

    只有两个数组A和B,size都是k 现在要从A和B中各选一个元素,取和 ,找出最小的k个和

    eg:

    1 8 5
    9 2 5

    我们先排序
    1 5 8
    2 5 9
    这样最小的就很明显了 肯定是3

    现在的问题就是比如 An+B1和A2+B2哪个小,因为An>A2,B1<B2


    思想就是先把A[i]+B[0]入队,每次弹出最小的,再入队下一个,B[0+1],这里的b就是B[b],前提b没有超出size,b+1<size

    3 7 10

    2 5 9


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

    表1:A1+B1<=A1+B2<=A1+B3……

    表2:A2+B1<=A2+B2<=A2+B3……

    表n:An+B1<=An+B2<=An+B3……

    首先将第一列入队,第一列的最小值即为全局的最小值;接下来定位到最小值的那一行,跳过这一个元素,把下一个元素入队,每次尽量贪接近最小的元素的那个位置的元素入队列,新入队在优先队列里会和早进队的大佬门比较。关键先把第一列入队后,每个数组的元素都可以入队了,都可以进行比较。首元素即为全局下一小的元素和,记录即可。 

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

    k×k×k的立方体表格



    用二元组来表示一个元素(s,b),s = Aa + Bb,s(下一个) = Aa + B(b+1) = Aa + Bb - Bb +B(b+1) = s - Bb + B(b+1),所以不需要a的下标,入队的时候直接计算,b处+1即可。


    这样合并两路存储在A数组后。在用A数组和其他路进行合并。A数组已经是合并了2路的结果,继续这样贪心合并。

    各路的合并是互不影响的,开始一个错误的思路是先取出每组最小的,这样相加肯定是最小的,存下来。然后每次换一个尽量小的。但是其实是可以同时换两个最小的,其他数组有的话甚至同时换三个最小的才是最后k个数中最小的。。

    所以还是多路归并的思想好,在队列里慢慢合并,反正每次取出的都是最小的更新C数组。

    注意这个题的思想是每个数组都要选一个,各个数组的和,所有的这些和前k小的          

    解题思想是关键。


    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 750 + 20;
    int n,A[maxn],B[maxn];
    struct Item{
      int s,b;//(s,b) s = Aa + Bb
      Item (){}
      Item(int s,int b):s(s) , b(b){}//构造函数
      bool operator < (const Item& rhs) const{//定义优先级
        return s > rhs.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),只用找前n个
        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(){
     freopen("in.txt","r",stdin);
     while(~scanf("%d",&n)){
        for(int i = 0; i < n; i++) //先读一路数据
           scanf("%d",&A[i]);
        sort(A , A + n);
        for(int i = 1; i < n; i++){ //以二路为基础做多路归并,n-1次归并
            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("
    ");
     }
     return 0;
    }
    




  • 相关阅读:
    1062 Talent and Virtue (25 分)
    1083 List Grades (25 分)
    1149 Dangerous Goods Packaging (25 分)
    1121 Damn Single (25 分)
    1120 Friend Numbers (20 分)
    1084 Broken Keyboard (20 分)
    1092 To Buy or Not to Buy (20 分)
    数组与链表
    二叉树
    时间复杂度与空间复杂度
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/7256647.html
Copyright © 2011-2022 走看看