P1631 序列合并
解题思路
暴力解法的缺陷
直接计算所有N²个和然后排序的时间复杂度是O(N²logN),对于N=1e5的情况显然不可行。
优化思路
-
利用序列单调性:由于A和B都是单调不降的,可以避免不必要的计算
-
优先队列维护:使用大根堆来维护当前找到的最小的N个和
-
剪枝策略:当发现当前计算的和已经大于堆顶元素时,可以提前终止内层循环
具体步骤
-
对两个序列进行排序(题目虽说是单调不降,但实际输入可能未排序)
-
初始化:将A[1]与B的所有元素相加的结果存入优先队列
-
遍历处理:对于A[2]到A[N],计算其与B元素的组合:
-
如果当前和≥堆顶元素,则后续组合必然更大,可以跳过
-
否则,替换堆顶元素
-
-
输出结果:将堆中元素取出并按升序排列
时间复杂度分析
-
排序:O(NlogN)
-
堆操作:外层循环N次,内层循环均摊O(logN),总O(NlogN)
-
总体复杂度:O(NlogN),可以处理N=1e5的规模
代码注释
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n; int a[N], b[N]; int ans[N]; priority_queue<int> q; // 大根堆,维护当前最小的N个和 int main() { // 输入处理 cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++) cin >> b[i]; // 排序确保单调性 sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + n); // 初始化:将A[1]与所有B元素的和加入堆 for(int i = 1; i <= n; i++) { q.push(a[1] + b[i]); } // 处理A[2..N] for(int i = 2; i <= n; i++) { for(int j = 1; j <= n; j++) { int cur_sum = a[i] + b[j]; if(cur_sum >= q.top()) break; // 剪枝:后续的和只会更大 q.pop(); // 移除当前最大的和 q.push(cur_sum); // 加入更小的和 } } // 将结果存入数组并排序 for(int i = n; i >= 1; i--) { ans[i] = q.top(); q.pop(); } // 输出结果 for(int i = 1; i <= n; i++) { cout << ans[i] << " "; } return 0; }

浙公网安备 33010602011771号