Description
有两个长度都为N的序列A和B,在A和B中各取一个数相加可以得到N2个和,求这N2个和中最小的N个。
Input
第一行一个正整数N(1 <= N <= 100000)。
第二行N个整数Ai,满足Ai <= Ai+1且Ai <= 109
第三行N个整数Bi,满足Bi <= Bi+1且Bi <= 109
Output
输出仅有一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。
Sample Input
3
2 6 6
1 4 8
Sample Output
3 6 7
HINT
建议用最小堆实现。
题目链接:序列合并
参考链接:最小的N个和(优先队列)
最终AC代码如下:
#include<bits/stdc++.h> using namespace std; const int MAX=1e5+2; struct Node{ int index, sum; bool operator < (const Node &t) const{ return sum > t.sum; //优先队列表示按sum的大小排列 } }; int main(){ int i, n, a[MAX], b[MAX]; Node temp, now; priority_queue<Node> q; while(cin >> n){ for(i=0; i<n; i++) scanf("%d", &a[i]); for(i=0; i<n; i++){ scanf("%d", &b[i]); temp.index = 0; temp.sum = b[i] + a[temp.index]; //index表示a[]中的第几个数字 q.push(temp); } for(i=0; i<n; i++){ now = q.top(); q.pop(); if(now.index+1 < n){ temp.index = now.index + 1; temp.sum = now.sum - a[now.index] + a[temp.index]; q.push(temp); } printf("%d%c", now.sum, i==n-1?' ':' '); } } return 0; }
总结:一开始看见提示,用最小堆做,但是没想到具体的思路~自认为本题的难点在于,虽然两组数据都是分别单调不减的,但是要求和最小(分别从两个序列中取数,每个数可以重复利用)的前N个数,于是就有很多不确定性了。如果数据量不是很大的情况,也许可以暴力,但是此题显然不行。
参考别人的思路后,发现可以用优先队列做(平时优先队列用得少,真的没想起这思路)。自己对这种思路的理解大致如下:
首先,取a[]数组中的最小元素,也就是a[0],分别加上b[i](0<=i<n),并放入优先队列中。那么可以确定,此时优先队列队首的元素,也即a[0]+b[0]的值就是最小的,可以输出,并将a[1]+b[0]输入优先队列,依次类推,直到输出了n个数为止。
上述过程,逻辑看似简单,但是要注意几点:
1、优先队列存放的元素是结构体变量。定义结构体的目的,结构体内sum变量存放两个数之和,index记录a[]的下标,从而为后面每次更新sum埋伏笔。
2、由于优先队列存放的元素是结构体变量,因此需要自定义比较的容器~然而这种定义方式着实不易理解,暂且先记住。
3、入队前对index的范围的判断,在此题可以省略(试了一下,没啥影响)。