2013多校第四场 B题
题意:
两个都含有N个元素的数组a,b, 求 a[i]+b[j] ( i , j < N ) 的前 M个最小值(M < N)
解题思路:
假定a,b数组都有序,则我们有下面的结论:
a_1 + b_1 <= a_1 + b_2 <= ... <= a_1 + b_n
a_2 + b_1 <= a_2 + b_2 <= ... <= a_2 + b_n
...
a_n + b_1 <= a_n + b_2 <= ... <= a_n + b_n
那么我们就可以得出一个 N*N 的矩阵.如下形式
A(1,1), A(1,2), ..., A(1,n)
A(2,1), A(2,2), ... ,A(2,n)
...
A(n,1), A(n,2), ... ,A(n,n)
我们知道,第一个最小的必定是 A(1,1),
而且. 从 A(1,1) 位置往下(A(2,1)) 或者 往右(A(1,2)), 其值必定增大.
但是我们又不知道 A(1,2) 与 A(2,1) 之间的关系.
不过, 我们可以想到:
若第k小的数是 A(i,j), 则第 k+1小的数可能是A(i+1,j)与A(i,j+1) 之间的数.
因为还有可能是在 其他的 A( a,b ) 的下或者右中..
又因为 我们只需要取前 M <= N 个数.所以我们使用一个容器,让里头保存N个数.
每次从容器中取出一个最值( A(a,b) ), 则再将其下方( A(a+1,b) ),与右边( A(a,b+1) ) 的值放入容器中.
这个容器我们可以选 堆/优先队列:
直接用 C++的STL. priority_queue< node, vector<node>, compare > 详细用法请百度.
编码细节:
我们可以预处理把 A(1,1), A(1,2), ...,A(1,n) 这N个数放到堆中, 每次取出最小的A(a,b)时,
则只需要将 A(a+1,b) 加入到堆即可. 因为 A(a,b+1)已经在堆中了.
这样.始终维护着堆中包含N个元素. 当输出满足M个了就结束即可.
算法时间复杂度:
优先队列的 插入与删除操作都是 Log(N). 总共N个元素.
则总体时间复杂度: NlogN
#include<cstdio> #include<cstdlib> #include<queue> #include<algorithm> #include<vector> using namespace std; const int N = 4e5+10; int a[N], b[N], res[N]; int n, m; struct Node{ int x, y, v; bool operator < (const Node &tmp) const{ return v > tmp.v; } }node; priority_queue<Node> Q; int main(){ freopen("1002.in","r",stdin); freopen("test.out","w",stdout); while( scanf("%d%d", &n, &m) != EOF){ int cnt = 0; for(int i = 0; i < n; i++) scanf("%d", &a[i]); for(int i = 0; i < n; i++) scanf("%d", &b[i]); sort( a, a+n ); sort( b, b+n ); while( !Q.empty() ) Q.pop(); for(int i = 0; i < n; i++){ node.x = 0; node.y = i; node.v = a[0]+b[i]; Q.push(node); } for(int i = 0; i < m; i++){ node = Q.top(); Q.pop(); printf("%d\n", node.v ); node.x++; node.v = a[ node.x ] + b[ node.y ]; Q.push( node ); } } return 0; }