题目大意 有k个长度为k的数组,从每个数组中选出1个数,再把这k个数进行求和,问在所有的这些和中,最小的前k个和。
考虑将前i个数组合并,保留前k个和。然后考虑将第(i + 1)个数组和它合并,保留前k个和。
如果暴力的话就进行就暴力枚举每一对,然后进行求和,然后再选出前k个,然而这样会TLE。
可以考虑将另外一个数组进行排序。然后可以看做是k个已经排好序的数组进行归并
{A[] + B[], A[] + B[], ...}
{A[] + B[], A[] + B[], ...}
{A[] + B[], A[] + B[], ...}
.
.
.
{A[k] + B[1], A[k] + B[], ...}
对于每个数组,只有前一个值被取了,后一个值才有可能被取。
所以用一个优先队列进行维护,先将所有数组的第一个元素放进去,然后每次取出一个元素,再将它的后一个放入队列。
每次合并时间复杂度O(klogk),所以总时间复杂度为O(k2logk)
Code
/**
* UVa
* Problem#11997
* Accepted
* Time: 190ms
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; int k;
int A[], B[], C[];
int *X, *Y; typedef class Item {
public:
int x, b; Item(int x = , int b = ):x(x), b(b) { } boolean operator < (Item xb) const {
return X[x] + B[b] > X[xb.x] + B[xb.b];
} int getVal() {
return X[x] + B[b];
}
}Item; inline void merge() {
priority_queue<Item> que;
for(int i = ; i < k; i++)
que.push(Item(i, )); for(int i = ; i < k; i++) {
Item e = que.top();
que.pop();
Y[i] = e.getVal();
que.push(Item(e.x, e.b + ));
}
while(!que.empty()) que.pop();
swap(X, Y);
} inline boolean init() {
if(scanf("%d", &k) == EOF) return false;
X = A, Y = C;
for(int i = ; i < k; i++)
scanf("%d", X + i);
sort(A, A + k);
for(int i = ; i < k; i++) {
for(int j = ; j < k; j++)
scanf("%d", B + j);
sort(B, B + k);
merge();
}
return true;
} inline void solve() {
for(int i = ; i < k - ; i++)
printf("%d ", X[i]);
printf("%d\n", X[k - ]);
} int main() {
while(init()) {
solve();
}
return ;
}