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