UVA - 11997 K Smallest Sums 归并排序+优先队列

题目大意:有k个数组,每个数组选出一个数相加,相加后结果放到一个新的数组里,这样就这个数组就有k^k个数了,输出这个新的数组 最小的k个数

解题思路:k^k,这样就不能暴力了,用归并排序

这里说一下简单化的同类型问题

问题为:假设有两个数量为n的数组,A和B,每个数组选出一个数相加并组成一个新的数组,输出这个新的数组的前n个最小值
先排序,然后暴力写出一下所有情况
A0 + B0 <= A0 + B1 <= A0 + B2 <= … <= A0 + Bn-1
A1 + B0 <= A1 + B1 <= A1 + B2 <=… <= A1 + Bn-1

An-1 + B0 <= An-1 + B1 <= … <= An-1 + Bn-1
我们可以将这些进行合并,设(S,b)为 Sb = Aa + Bb,如果想要得到下一个元素(S’,b+1),我们可以通过S’ = Aa + Bb+1 + Bb - Bb = Sb - Bb + Bb+1,这样就可以通过合并来得到数组的前n个的最小和了,只需要一个个数组进行合并就可以了
然后我们通过优先队列进行筛选,筛选出前n个,具体看代码,这里说得不是很详细

#include
#include
#include
#include
using namespace std;
#define maxn 760
int A[maxn][maxn];
struct Node {
    int s, b;
    Node (int s, int b) :s(s), b(b) {}
    bool operator < (const Node &t) const {
        return s > t.s; 
    }
};

void merge(int *A, int *B , int *C, int n) {
    priority_queue pq;
    for(int i = 0; i < n; i++)
        pq.push(Node(A[i]+B[0],0));
    for(int i = 0; i < n; i++) {
        Node node = pq.top();
        pq.pop();
        C[i] = node.s;
        int b = node.b;

        if(b + 1 < n)
            pq.push(Node(C[i]-B[b]+B[b+1],b+1));    
    }
}

int main() {
    int n;
    while(scanf("%d", &n) == 1) {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++)
                scanf("%d", &A[i][j]);
            sort(A[i],A[i]+n);  
        }
        for(int i = 1; i < n; i++)
            merge(A[0],A[i],A[0],n);
        printf("%d", A[0][0]);

        for(int i = 1; i < n; i++)
            printf(" %d", A[0][i]);
        printf("\n");
    }
}

你可能感兴趣的:(ACM-数据结构)