news 2026/5/29 5:08:59

PTA L2-045 堆宝塔题解:用两个栈模拟游戏过程,保姆级图解+代码逐行分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PTA L2-045 堆宝塔题解:用两个栈模拟游戏过程,保姆级图解+代码逐行分析

PTA L2-045 堆宝塔:双栈模拟的艺术与实战拆解

当彩虹圈遇上数据结构,一场关于栈的思维体操就此展开。这道PTA天梯赛L2级别的题目,表面上是儿童游戏模拟,实则是数据结构应用的绝佳案例。我们将从游戏规则解析入手,逐步拆解如何用两个栈(或向量)完美模拟"堆宝塔"的全过程。

1. 游戏规则与数据结构映射

堆宝塔游戏的核心在于两根柱子的协同操作——这正是栈结构"后进先出"特性的完美体现。让我们先理清题目描述的每一个动作对应的数据结构操作:

  • A柱:主栈,用于构建当前正在堆叠的宝塔。每次只能从顶部添加或移除彩虹圈,完全符合栈的特性。
  • B柱:辅助栈,用于临时存放不符合当前主栈条件的彩虹圈。当主栈需要重置时,B柱中的部分元素会转移回A柱。

游戏的关键操作流程可以抽象为以下步骤:

  1. 初始化两个空栈A和B
  2. 读取下一个彩虹圈直径C
  3. 如果A为空,直接将C压入A栈
  4. 否则比较C与A栈顶元素:
    • 如果C更小,压入A栈
    • 否则检查B栈:
      • 如果B为空或C大于B栈顶,压入B栈
      • 否则:
        • 将当前A栈作为成品保存
        • 清空A栈
        • 将B栈中所有大于C的元素转移到A栈
        • 最后将C压入A栈
  5. 重复2-4直到所有彩虹圈处理完毕
  6. 将剩余的A栈和B栈(如有)作为最终成品

2. 代码实现逐行解析

让我们深入分析参考代码的每一部分,理解如何将上述逻辑转化为C++实现:

#include<bits/stdc++.h> using namespace std; int main() { vector<vector<int>> res; // 存储所有完成的宝塔 vector<int> a, b; // a模拟A柱,b模拟B柱 int n; cin >> n; while(n--) { int x; cin >> x; if(a.empty()) { a.push_back(x); // 规则3:A柱为空时直接放入 } else if(x < a.back()) { a.push_back(x); // 规则4a:比A栈顶小,直接压入 } else if(b.empty() || x > b.back()) { b.push_back(x); // 规则4b:符合B柱条件时压入B栈 } else { res.push_back(a); // 保存当前A栈为成品 a.clear(); // 清空A栈准备重建 // 转移B栈中大于x的元素到A栈 while(!b.empty() && b.back() > x) { a.push_back(b.back()); b.pop_back(); } a.push_back(x); // 最后压入当前彩虹圈 } } // 处理最后剩余的宝塔 if(!a.empty()) res.push_back(a); if(!b.empty()) res.push_back(b); // 计算最大层数 int mx = 0; for(auto &c : res) { mx = max(mx, (int)c.size()); } cout << (int)res.size() << " " << mx; return 0; }

关键操作点详解

  1. 成品保存机制

    • 使用vector<vector<int>> res存储所有完成的宝塔
    • 每当需要重置A柱时,将当前a向量存入res
    • 最后处理剩余未完成的a和b
  2. B栈元素转移

    while(!b.empty() && b.back() > x) { a.push_back(b.back()); b.pop_back(); }
    • 这个循环确保只转移B栈中大于当前彩虹圈直径的元素
    • 保持转移后A栈依然满足从大到小的顺序
  3. 边界条件处理

    • 初始空栈处理
    • 最终剩余栈处理
    • 空输入处理(虽然题目保证N≥1)

3. 实例推演与状态转换

让我们用题目给出的样例进行逐步推演,直观理解双栈如何协同工作:

输入样例

11 10 8 9 5 12 11 4 3 1 9 15

推演过程

步骤当前彩虹圈A柱状态B柱状态操作说明
110[10][]初始放入A柱
28[10,8][]8<10,直接放入A
39[10][9]9>8,B为空,放入B
45[10,5][9]5<9不成立,5<10,直接放入A
512[12][9]12>10,保存A=[10,5],转移B>12无,放入A
611[12,11][9]11<12,直接放入A
74[12,11,4][9]4<11,直接放入A
83[12,11,4,3][9]3<4,直接放入A
91[12,11,4,3,1][9]1<3,直接放入A
109[9][]9>1,保存A=[12,11,4,3,1],转移B>9无,放入A
1115[15][9]15>9,B为空,放入B

最终处理

  • 保存A=[15]为成品
  • 将B=[9]转为宝塔[9]保存

成品宝塔

  1. [10,5]
  2. [12,11,4,3,1]
  3. [15]
  4. [9]

4. 算法优化与边界思考

虽然题目给出的数据规模(N≤1000)使得O(N²)的解法完全可行,但我们仍可以思考可能的优化空间和边界情况:

  1. 时间复杂度分析

    • 每个元素最多被压入和弹出各两次(A栈和B栈)
    • 整体时间复杂度为O(N)
    • 最坏情况下(如完全逆序输入)会频繁触发宝塔保存操作
  2. 空间复杂度

    • 使用两个栈存储当前元素,O(N)空间
    • 结果存储所有宝塔,最坏情况下O(N)空间
  3. 易错点警示

    • 初始条件检查:忘记处理第一个元素的特殊情况
    • 栈空判断:在访问栈顶元素前未检查栈是否为空
    • 元素转移条件:错误地将B栈中所有元素而非仅大于当前值的元素转移
    • 最终处理:遗漏处理循环结束后A栈和B栈中剩余的元素
  4. 代码可读性优化

    • 使用更具描述性的变量名(如main_stack/temp_stack)
    • 将核心逻辑封装为独立函数
    • 添加注释说明关键决策点
// 优化后的函数封装版本 void process_rainbow_ring(int x, vector<int>& main_stack, vector<int>& temp_stack, vector<vector<int>>& result) { if(main_stack.empty()) { main_stack.push_back(x); } else if(x < main_stack.back()) { main_stack.push_back(x); } else if(temp_stack.empty() || x > temp_stack.back()) { temp_stack.push_back(x); } else { result.push_back(main_stack); main_stack.clear(); while(!temp_stack.empty() && temp_stack.back() > x) { main_stack.push_back(temp_stack.back()); temp_stack.pop_back(); } main_stack.push_back(x); } }

5. 栈结构在算法题中的常见应用模式

通过这道题,我们可以总结栈在算法问题中的几种典型应用场景:

  1. 模拟系统

    • 如本题模拟物理堆叠过程
    • 浏览器前进后退功能
    • 文本编辑器的撤销重做
  2. 括号匹配

    • 检查表达式中的括号是否合法嵌套
    • 计算嵌套深度
  3. 单调栈

    • 解决"下一个更大元素"类问题
    • 柱状图最大矩形面积计算
  4. 函数调用栈

    • 程序执行时的函数调用关系
    • 递归函数的非递归实现

栈选择技巧对比表

场景特征适用数据结构原因典型例题
后进先出的物理过程天然匹配操作顺序堆宝塔、汉诺塔
需要频繁访问最近元素直接访问栈顶O(1)括号匹配、表达式求值
需要维护某种单调性单调栈可以高效维护区间极值每日温度、最大矩形面积
需要双向操作双栈可以模拟队列或提供更多灵活性用栈实现队列、本题堆宝塔

掌握栈的这种"后进先出"特性,能够帮助我们更自然地模拟许多现实世界的过程。在解决算法问题时,当遇到以下关键词时,就可以考虑栈是否适用:

  • "最近匹配"、"嵌套结构"、"撤销操作"、"分层处理"、"顺序反转"等

回到本题,堆宝塔游戏完美展现了如何用栈来模拟具有明确顺序约束的物理过程。通过A、B双栈的协同,我们能够高效地管理彩虹圈的暂存和转移,这正是数据结构抽象现实问题的魅力所在。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 5:08:32

LLM Ops实战指南:构建大语言模型应用的工程化运维体系

1. 项目概述&#xff1a;当DevOps遇见大语言模型如果你和我一样&#xff0c;在过去几年里深度参与了AI项目的落地&#xff0c;尤其是那些围绕大语言模型的应用&#xff0c;你肯定经历过这样的场景&#xff1a;好不容易在本地用开源模型或者API跑通了一个惊艳的Demo&#xff0c;…

作者头像 李华
网站建设 2026/5/29 5:06:00

如何在5分钟内搭建你的AI股票分析系统:TradingAgents-CN完整指南

如何在5分钟内搭建你的AI股票分析系统&#xff1a;TradingAgents-CN完整指南 【免费下载链接】TradingAgents-CN 基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版 项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN 想在几分钟内拥有专…

作者头像 李华
网站建设 2026/5/29 5:05:58

Qwen-Scope高级技巧:自定义特征强度与生成控制全攻略

Qwen-Scope高级技巧&#xff1a;自定义特征强度与生成控制全攻略 【免费下载链接】SAE-Res-Qwen3.5-9B-Base-W64K-L0_50 项目地址: https://ai.gitcode.com/hf_mirrors/Qwen/SAE-Res-Qwen3.5-9B-Base-W64K-L0_50 Qwen-Scope是一款强大的SAE&#xff08;稀疏自编码器&am…

作者头像 李华
网站建设 2026/5/29 5:05:02

Cortex-M处理器LOCKUP机制与动态信号处理

1. Cortex-M系列处理器中的LOCKUP机制解析在Cortex-M系列处理器架构中&#xff0c;LOCKUP状态是一种特殊的错误处理机制。当处理器检测到某些严重错误&#xff08;如双重故障&#xff09;时&#xff0c;会进入LOCKUP状态并拉高LOCKUP信号线。这个设计初衷是为了在系统出现不可恢…

作者头像 李华
网站建设 2026/5/29 5:03:01

别再死记硬背!用Python的SymPy库5分钟搞定拉氏反变换(附代码)

用Python的SymPy库5分钟搞定拉氏反变换&#xff1a;工程师的高效计算指南在信号处理和控制系统的日常工作中&#xff0c;拉普拉斯反变换是每个工程师都无法绕开的数学工具。传统的手工计算不仅耗时费力&#xff0c;还容易在部分分式分解和留数计算环节出错。想象一下&#xff0…

作者头像 李华