牛客 Sequence

https://ac.nowcoder.com/acm/contest/1011/B

题面

给定M个长度为N的序列,从每个序列中任取一个数求和,可以构成\(N^M\)个和,求最小的N个和
\(N\leq 2000,M\leq 100\)

分析

显然合并\(N\)个是\(N^M\)
可以先考虑合并2个
对于两个数列\({a_n},{b_n}\)
可以有

\[ \left[ \begin{matrix} a_1+b_1 & a_1+b_2 & a_1+b_3 & \ldots\\ a_2+b_1 & a_2+b_2 & a_2+b_3 & \ldots\\ a_3+b_1 & a_3+b_2 & a_3+b_3 & \ldots \end{matrix} \right] \]

每一行都是单调增
所以只需要将每一行第一个加入堆中,\(O(lgn)\)维护,取出前N小和即可
合并N个可以先合并2个,再合并2个,\(\ldots\)

#include<bits/stdc++.h>
using namespace std;

const int N=2006;
int n,m,a[N],b[N];
struct A{int x, y; };
bool operator >(A i,A j) {
	return i.x>j.x;
}
priority_queue<A,vector<A>,greater<A> >q;

int main() {
	int T; scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++) {
			scanf("%d",&a[i]);
		}
		sort(a+1,a+m+1);
		for(int i=1;i<=m;i++) b[i]=a[i];
		for(int i=2;i<=n;i++) {
			for(int j=1;j<=m;j++) {
				scanf("%d",&a[j]);
			}
			sort(a+1,a+m+1);
			for(int j=1;j<=m;j++) {
				q.push((A){b[j]+a[1],1});
			}
			for(int j=1;j<=m;j++) {
				A u=q.top(); q.pop();
				b[j]=u.x; 
				q.push((A){u.x-a[u.y]+a[u.y+1],u.y+1});
			}
			for(int j=1;j<=m;j++) q.pop();
		}
		for(int i=1;i<=m;i++) {
			printf("%d%c",b[i],i==m?'\n':' ');
		}
	}
	return 0;
}
posted @ 2020-11-27 14:22  wwwsfff  阅读(83)  评论(0)    收藏  举报