洛谷P1631序列合并传送门
对于这一题的用堆解答的思路, 排名第二的答案已经说得很清楚了, 排名第一的答案不仅没说清楚, 代码写的也是low的不行, 下面搬运一下赞数第二的大神的思路以及我的实现方法
从A和B中各任取一个数相加得到N2个和,可以把这些和看成形成了n个有序表/队列:
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个队列中的第一个元素放入一个堆中;
然后;每次取出堆中的最小值。若这个最小值来自于第k个队列,那么,就将第k个队列的下一个元素放入堆中。
由图意,因为每一行每一列, 都是按顺序从小到大,所以 a1+b1<a2+b1; a1+b1<a1+b2; 即按着箭头的方向从小到大,但不能判断a1+b2和a2+b1谁大,所以呢,他们想出来一个办法,最开始把第一行 a1+b1,a2+b1,a3+b1,a4+b1,a5+b1全部压入堆,此时想当然a1+b1这个最小,把这个弹出来输出,再把下面的a1+b2压进去,这个时候,自然就能判断他与a2+b1谁大谁小,所以呢,再将最小的取出,将他下面的那个压入。
上面的分析已经非常清楚了, 下面说一说我的实现
#include <bits/stdc++.h>
using namespace std;
int n, cnt;
const int MAXN = 100000 + 10;
int a[MAXN], b[MAXN];
struct Node {
int i;
int j;
int val;
bool operator <(const Node& b) const {
return val > b.val;
}
};
priority_queue <Node, vector<Node> > q; //小根堆
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
for (int i = 1; i <= n; i++) {
q.push(Node {i, 1, a[i] + b[1]});
}
while (cnt < n) {
Node t = q.top();
q.pop();
cout << t.val << " ";
int i = t.i;
int j = t.j;
q.push(Node{i, j + 1, a[i] + b[j + 1]});
cnt++;
}
return 0;
}
应该非常浅显易懂吧!