UVa 11997 K Smallest Sums - 优先队列
题目大意 有k个长度为k的数组,从每个数组中选出1个数,再把这k个数进行求和,问在所有的这些和中,最小的前k个和。
考虑将前i个数组合并,保留前k个和。然后考虑将第(i + 1)个数组和它合并,保留前k个和。
如果暴力的话就进行就暴力枚举每一对,然后进行求和,然后再选出前k个,然而这样会TLE。
可以考虑将另外一个数组进行排序。然后可以看做是k个已经排好序的数组进行归并
{A[1] + B[1], A[1] + B[2], ...} {A[2] + B[1], A[2] + B[2], ...} {A[3] + B[1], A[3] + B[2], ...} . . . {A[k] + B[1], A[k] + B[2], ...}
对于每个数组,只有前一个值被取了,后一个值才有可能被取。
所以用一个优先队列进行维护,先将所有数组的第一个元素放进去,然后每次取出一个元素,再将它的后一个放入队列。
每次合并时间复杂度O(klogk),所以总时间复杂度为O(k2logk)
Code
1 /** 2 * UVa 3 * Problem#11997 4 * Accepted 5 * Time: 190ms 6 */ 7 #include <bits/stdc++.h> 8 using namespace std; 9 typedef bool boolean; 10 11 int k; 12 int A[751], B[751], C[751]; 13 int *X, *Y; 14 15 typedef class Item { 16 public: 17 int x, b; 18 19 Item(int x = 0, int b = 0):x(x), b(b) { } 20 21 boolean operator < (Item xb) const { 22 return X[x] + B[b] > X[xb.x] + B[xb.b]; 23 } 24 25 int getVal() { 26 return X[x] + B[b]; 27 } 28 }Item; 29 30 inline void merge() { 31 priority_queue<Item> que; 32 for(int i = 0; i < k; i++) 33 que.push(Item(i, 0)); 34 35 for(int i = 0; i < k; i++) { 36 Item e = que.top(); 37 que.pop(); 38 Y[i] = e.getVal(); 39 que.push(Item(e.x, e.b + 1)); 40 } 41 while(!que.empty()) que.pop(); 42 swap(X, Y); 43 } 44 45 inline boolean init() { 46 if(scanf("%d", &k) == EOF) return false; 47 X = A, Y = C; 48 for(int i = 0; i < k; i++) 49 scanf("%d", X + i); 50 sort(A, A + k); 51 for(int i = 1; i < k; i++) { 52 for(int j = 0; j < k; j++) 53 scanf("%d", B + j); 54 sort(B, B + k); 55 merge(); 56 } 57 return true; 58 } 59 60 inline void solve() { 61 for(int i = 0; i < k - 1; i++) 62 printf("%d ", X[i]); 63 printf("%d\n", X[k - 1]); 64 } 65 66 int main() { 67 while(init()) { 68 solve(); 69 } 70 return 0; 71 }