UVa 11997 (优先队列 多路归并) K Smallest Sums
考虑一个简单的问题,两个长度为n的有序数组A和B,从每个数组中各选出一个数相加,共n2中情况,求最小的n个数。
将这n2个数拆成n个有序表:
A1+B1≤A1+B2≤...
A2+B1≤A2+B2≤...
...
An+B1≤An+B2≤...
然后用优先队列合并成一个有序表即可。队列中需要记录两个数的和s,以及在B中的下标b,
比如Aa+Bb出队以后,应该将Aa+B(b+1) = Aa + Bb - Bb + Bb+1 = s - Bb + bb+1入队
对于书上最后的一个思考问题,merge函数中对A[0]又读又写,会不会出错。答案当然是不会的,因为进入到函数内部你会发现,对A数组其实是先读后写的。
1 #include <cstdio> 2 #include <queue> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 750 + 10; 7 int a[2][maxn]; 8 9 void scan(int& x) 10 { 11 char c; 12 while(c = getchar(), c < '0' || c > '9'); 13 x = c - '0'; 14 while(c = getchar(), c >= '0' && c <= '9') x = x*10 + c - '0'; 15 } 16 17 struct Node 18 { 19 int sum, b; 20 Node(int s, int b):sum(s), b(b) {} 21 bool operator < (const Node& rhs) const 22 { return sum > rhs.sum; } 23 }; 24 25 void merge(int n, int* A, int* B, int* C) 26 { 27 priority_queue<Node> Q; 28 for(int i = 0; i < n; i++) Q.push(Node(A[i]+B[0], 0)); 29 30 for(int i = 0; i < n; i++) 31 { 32 Node t = Q.top(); Q.pop(); 33 int b = t.b, sum = t.sum; 34 C[i] = sum; 35 if(b + 1 < n) Q.push(Node(sum-B[b]+B[b+1], b+1)); 36 } 37 } 38 39 int main() 40 { 41 //freopen("in.txt", "r", stdin); 42 43 int k; 44 while(scanf("%d", &k) == 1) 45 { 46 for(int i = 0; i < k; i++) scan(a[0][i]); 47 sort(a[0], a[0] + k); 48 for(int i = 1; i < k; i++) 49 { 50 for(int j = 0; j < k; j++) scan(a[1][j]); 51 sort(a[1], a[1] + k); 52 merge(k, a[0], a[1], a[0]); 53 } 54 55 for(int i = 0; i < k; i++) 56 { 57 if(i) printf(" "); 58 printf("%d", a[0][i]); 59 } 60 puts(""); 61 } 62 63 return 0; 64 }