UVA ~ 11997 ~ K Smallest Sums (优先队列 + 多路归并)

题意:有k个整数数组,各包含k个元素。在每个数组中取一个元素加起来,可以得到k^k个和。求这些和中最小的k个值(重复的值算多次)。

【输入格式】

输入包含多组数据。每组数据第一行为一个整数k(1<=k<=750)。以下k行每行包含k个不超过10^6的正整数。输入结束标志为EOF。输入文件不超过5MB。

【输出格式】

对于每组数据,输出k个最小和的值,并按照从小到大排序。

【分析】

在解决这个问题之前,先看他的简化版:给出两个长度为n的有序表A和B,分别在A和B中任取一个数并相加,可以得到n^2个和。求这些和中最小的n个。

这个问题可以转化为前面介绍过的多路归并问题。这需要我们把这n^2个和组织成如下n个有序表。

表1:A1+B1≤A1+B2≤A1+B3≤...

表2:A2+B1≤A2+B2≤A2+B3≤...

表3:An+B1≤An+B2≤An+B3≤...

其中第a张表里的元素形如Aa+Bb。我们用二元组(s,b)来表示一个元素,其中s=Aa+Bb。为什么不保存A的下标a呢?因为我们用不到a的值。如果我们需要得到一个元素(s,b)在表a中的下一个元素(s',b+1),只需要计算s'=Aa+B(b+1)=Aa+Bb-Bb+B(b+1)=s-Bb+B(b+1),并不需要知道a是多少。代码里可以用如下结构体来表示。

struct Item
{
    int s, b;//s=A[a]+B[b]
    Item (int _s, int _b): s(_s), b(_b) {}
    bool operator < (const Item& that) const
    {
        return that.s < s;
    }
};

以上内容来自算法竞赛入门经典

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 755;
struct Item
{
    int s, b;
    Item (int _s, int _b): s(_s), b(_b) {}
    bool operator < (const Item& that) const
    {
        return that.s < s;
    }
};
void Merge(int* A, int* B, int* C, int n)
{
    priority_queue<Item> PQ;
    for (int i = 0; i < n; i++)
        PQ.push(Item(A[i]+B[0], 0));
    for (int i = 0; i < n; i++)
    {
        Item t = PQ.top(); PQ.pop();
        C[i] = t.s;
        int b = t.b;
        if (b+1 < n) PQ.push(Item(t.s-B[b]+B[b+1], b+1));
    }
}
int n, A[MAXN][MAXN];
int main()
{
    while (~scanf("%d", &n))
    {
        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);
        for (int i = 0; i < n; i++)
        {
            printf("%d", A[0][i]);
            if (i != n-1) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}
/*
3
1 8 5
9 2 5
10 7 6
2
1 1
1 2
*/


空间优化:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 755;
struct Item
{
    int s, b;//s=A[a]+B[b]
    Item (int _s, int _b): s(_s), b(_b) {}
    bool operator < (const Item& that) const
    {
        return that.s < s;
    }
};
void Merge(int* A, int* B, int* C, int n)
{
    priority_queue<Item> PQ;
    for (int i = 0; i < n; i++)
        PQ.push(Item(A[i]+B[0], 0));
    for (int i = 0; i < n; i++)
    {
        Item t = PQ.top(); PQ.pop();
        C[i] = t.s;
        int b = t.b;
        if (b+1 < n) PQ.push(Item(t.s-B[b]+B[b+1], b+1));
    }
}
int n, A[2][MAXN];
int main()
{
    while (~scanf("%d", &n))
    {
        for (int i = 0; i < n; i++) scanf("%d", &A[0][i]);
        for (int i = 1; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                scanf("%d", &A[1][j]);
            }
            sort(A[1], A[1] + n);
            Merge(A[0], A[1], A[0], n);
        }
        for (int i = 0; i < n; i++)
        {
            printf("%d", A[0][i]);
            if (i != n-1) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}
/*
3
1 8 5
9 2 5
10 7 6
2
1 1
1 2
*/


猜你喜欢

转载自blog.csdn.net/zscdst/article/details/80186274