题意:有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
*/