Uva11997

题意

给你 k 个数组,在每个数组中取出一个元素可以得到 k^k 个数,求这其中最小的 k 个数


 数据范围显然是不能暴力的,想作法也只有一些乱搞做法

首先考虑只有两个数组的情况,就是当 k = 2 ,数组长度为 n 时,求出这其中最小的 n 个值

首先排序,列出一下一些表:

a1 + b1, a1 + b2, a1 + b3, ...

a2 + b1, a2 + b2, a2 + b3, ...

a3 + b1, a3 + b2, a3 + b3, ...

...

an + b1, an + b2, an + b3, ...

我们也很容易在这些行/列之间画出小于号

这样一个位置上的数可能成为答案集合当中的数当且仅当它左边或上边的数被选了

后边就脑残了= =

其实就把第一列全部塞进堆里,每个元素记录一个下标就好了,每次取出来这个数就把它右边的数也入堆

是 O(klogk) 的

现在考虑这道题

多维?

只需要每次合并两个

最后按大小找就好了


 

代码:

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

const int MAXK = 755;

struct Node{
    int sum, pos;
    Node(int s = 0, int b = 0){sum = s; pos = b;}
    bool operator < (const Node& b) const {
        return sum > b.sum;
    }
};
int k;
int a[MAXK][MAXK];

inline void Merge(int *x, int *y, int *dst) {
    priority_queue<Node> q;
    for(int i = 1; i <= k; ++i) q.push(Node(x[i] + y[1], 1));
    for(int i = 1; i <= k; ++i) {
        Node cur = q.top(); q.pop();
        dst[i] = cur.sum;
        ++cur.pos;
        cur.sum = cur.sum - y[cur.pos - 1] + y[cur.pos];
        if(cur.pos <= k) q.push(cur);
    }
    return;
}

int main() {
    while(scanf("%d", &k) != EOF) {
        for(int i = 1; i <= k; ++i) {
            for(int j = 1; j <= k; ++j) 
                scanf("%d", &a[i][j]);
            sort(a[i] + 1, a[i] + k + 1);
        }
        for(int i = 2; i <= k; ++i) {
            Merge(a[1], a[i], a[1]);
        }
        for(int i = 1; i < k; ++i) printf("%d ", a[1][i]);
        printf("%d\n", a[1][k]);
    }
    return 0;
}

  

posted @ 2018-09-25 07:23  EvalonXing  阅读(108)  评论(0编辑  收藏  举报