P1392 取数(优先队列)
类似两个序列合并求前n小。
开一个优先队列 保存当前 i − 1 i-1 i−1行的前 k k k小。
把队列里的数组丢到数组b里。
然后第i行排序后丢到数组a里。
问题就变成两个序列合并问题。
注意 q . t o p ( ) ≤ a [ j ] + b [ l ] q.top()\le a[j]+b[l] q.top()≤a[j]+b[l] 就可以退出了。
因为两个数组都是非递减的。
第二层和第三层循环是一个调和级数: ∑ i = 1 k k i ≈ k l o g k \sum\limits_{i=1}^k\dfrac{k}{i}\approx klogk i=1∑kik≈klogk
所以复杂度是 n m l o g 2 k nmlog^2k nmlog2k
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=805,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = {402653189,805306457,1610612741,998244353};
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
template <typename T> //x=max(x,y) x=min(x,y)
void cmx(T &x,T y){
if(x<y) x=y;
}
template <typename T>
void cmn(T &x,T y){
if(x>y) x=y;
}
priority_queue<int>q;
int a[N],b[N];
int bid,qid;
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
q.push(0);
rep(i,1,n){
rep(j,1,m) scanf("%d",&a[j]);
sort(a+1,a+m+1);
bid=qid=0; while(!q.empty()) b[++bid] = q.top(),q.pop();
for(int j=1;j<=m;j++){
for(int l=bid;l;l--){
if(qid<k) q.push(a[j]+b[l]),qid++;
else if(q.top()<=a[j]+b[l]) break;
else q.pop(),q.push(a[j]+b[l]);
}
}
}
bid = 0;
while(!q.empty()) b[++bid] = q.top(),q.pop();
for(int i=bid;i;i--) printf("%d ",b[i]);
return 0;
}