洛谷P1323题解:优先队列与单调栈解决删数问题

一、题目解读
洛谷P1323题要求生成前k个最小的元素,并通过删除m个数字使剩余序列单调递增。题目需结合生成算法与优化删除策略,考验对数据结构与贪心思想的运用。
二、解题思路
1. 生成最小k数:利用优先队列(最小堆)维护当前最小元素。每次取出队首元素,生成其衍生元素(2x+1和4x+5),避免重复后加入队列,直至得到k个元素。
2. 删除m数优化:将生成的数字拼接为字符串,采用单调栈策略。遍历字符时,若栈末尾数字小于当前数字且可删除次数剩余,则弹出栈顶直至满足条件,最终保留单调递增子序列。
三、解题步骤
1. 输入k和m,初始化优先队列pq(最小堆)并加入初始元素1。
2. 循环k次:
取出队首元素,若与上一元素重复则跳过。
加入队列的新元素为2x+1和4x+5,确保队列始终维护最小元素。
3. 将生成的k个元素拼接为字符串numStr。
4. 遍历numStr,利用单调栈删除:
若当前字符大于栈顶且可删除次数>0,弹出栈顶直至不满足条件。
将当前字符入栈,最终栈中序列即为删除m个数字后的最大单调递增子串。
5. 若剩余删除次数未用完,从末尾截断字符串。
6. 输出原始序列和优化后的结果。
四、代码与注释
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main() {
int k, m;
cin >> k >> m; // 输入k和m
// 优先队列(最小堆)生成k个最小元素
priority_queue<int, vector<int>, greater<int>> pq;
vector<int> elements;
pq.push(1); // 初始元素1
while(elements.size() < k) {
int current = pq.top(); // 取出最小元素
pq.pop();
// 避免重复元素
if (!elements.empty() && current == elements.back()) {
continue;
}
elements.push_back(current); // 加入有效元素
// 生成新元素加入队列
pq.push(2 * current + 1);
pq.push(4 * current + 5);
}
// 拼接数字为字符串
string numStr;
for (int num : elements) {
numStr += to_string(num);
}
cout << numStr << endl; // 输出原始序列
// 单调栈删除m个数字优化
string result;
int remove = m;
for (char digit : numStr) {
// 贪心删除:若当前数字比栈顶大且可删除
while (remove > 0 &&!result.empty() && digit > result.back()) {
result.pop_back(); // 弹出栈顶
remove--;
}
result.push_back(digit); // 加入当前数字
}
// 剩余删除次数从末尾截断
if (remove > 0) {
result = result.substr(0, result.size() - remove);
}
cout << result << endl; // 输出优化结果
return 0;
}五、总结
该解法巧妙结合优先队列与单调栈:前者高效生成有序序列,后者通过贪心策略删除元素确保单调性。代码简洁且逻辑清晰,避免复杂循环,时间复杂度低。解题时需注意重复元素的判断与删除操作的边界处理,是处理此类生成+优化问题的典型范例。
原创内容 转载请注明出处






