PAT 甲级 L3-009 实战:5步理解“视线相切”条件与栈维护凸点技巧

📅 2026/7/13 12:48:39
PAT 甲级 L3-009 实战:5步理解“视线相切”条件与栈维护凸点技巧
PAT 甲级 L3-009 实战5步理解“视线相切”条件与栈维护凸点技巧烽火台布局问题看似简单实则暗藏玄机。想象你站在蜿蜒的长城上如何用最少的哨塔覆盖所有北方区域这不仅是历史军事难题更是算法竞赛中经典的几何优化问题。本文将带您拆解题目中的视线相切条件并用单调栈这一利器高效解决凸点维护问题。1. 从军事需求到数学模型古代长城防御系统的核心在于视线覆盖——每个烽火台需要监控其北方所有未被山体遮挡的区域。题目中的关键约束是视线相切也算覆盖这实际上转化为了几何中的凸包性质问题。以图2为例当三个烽火台A、B、C共线时若只在C点设置烽火台其视线沿AC直线延伸根据相切规则AB段也被视为可监控区域因此B点不需要额外设置烽火台数学表达上判断三点A(x₁,y₁)、B(x₂,y₂)、C(x₃,y₃)是否构成凸点的条件为斜率(AB) ≥ 斜率(BC) → (y₂-y₁)/(x₂-x₁) ≥ (y₃-y₂)/(x₃-x₂)为避免浮点精度问题实际编码时应使用交叉相乘形式bool isConvex(int a, int b, int c) { return (points[b].y-points[a].y)*(points[c].x-points[b].x) (points[c].y-points[b].y)*(points[b].x-points[a].x); }2. 单调栈的魔法动态维护凸点序列直接遍历所有三点组合的时间复杂度是O(n³)对于n1e5的数据量完全不可行。这时就需要引入单调栈这一数据结构来优化。操作流程如下初始化空栈按从南到北顺序处理每个顶点对于新顶点P检查栈顶两个顶点S1、S2如果S2→S1→P形成凹点不满足斜率条件弹出S1重复步骤2直到栈中剩余1个元素或满足凸性条件将P压入栈中最终栈中元素即为需要建造烽火台的位置stackint st; for(int i 0; i n; i) { while(st.size() 2) { int top1 st.top(); st.pop(); int top2 st.top(); if(!isConvex(top2, top1, i)) { st.push(top1); break; } } st.push(i); }3. 边界条件与特殊测试用例实际编码时需要特别注意几种边界情况测试类型示例输入处理要点共线点(0,0),(1,1),(2,2)中间点不应计数垂直峭壁(0,0),(0,5),(1,3)斜率无穷大需特殊处理单峰地形(0,0),(1,3),(2,0)顶点必须设置烽火台锯齿地形交替上升下降点每个凸点都需检测提示在PAT测试用例中共线情况往往是最容易遗漏的边界条件。建议单独编写检查函数验证三点共线情况。4. 算法复杂度分析与优化原始暴力算法需要三重循环时间复杂度为O(n³)。采用单调栈优化后时间复杂度每个顶点最多入栈出栈一次整体O(n)空间复杂度只需维护一个栈最坏情况O(n)实际运行效果对比数据规模暴力算法单调栈优化n1001.2s0.001sn1e4超时0.015sn1e5无法运行0.18s5. 完整代码实现与调试技巧结合上述分析给出最终实现代码框架#include iostream #include stack #include vector using namespace std; struct Point { long long x, y; }; vectorPoint points; bool isConvex(int a, int b, int c) { Point p1 points[a], p2 points[b], p3 points[c]; return (p2.y-p1.y)*(p3.x-p2.x) (p3.y-p2.y)*(p2.x-p1.x); } int main() { int n; cin n; points.resize(n); for(int i0; in; i) cin points[i].x points[i].y; stackint st; vectorbool isTower(n, false); for(int i0; in; i) { while(st.size() 2) { int top1 st.top(); st.pop(); int top2 st.top(); if(!isConvex(top2, top1, i)) { st.push(top1); break; } } if(!st.empty()) isTower[st.top()] true; st.push(i); } int ans 0; for(int i1; in-1; i) if(isTower[i]) ans; cout ans; }调试时建议先用小规模数据验证基本逻辑打印栈状态辅助理解维护过程对特殊地形单独测试使用assert检查中间结果理解这个问题的关键在于将军事需求转化为几何条件再用数据结构优化计算过程。当看到最终算法高效运行时那种成就感就像当年长城上的哨兵成功预警敌情一样令人振奋。