PAT L3-009 长城 30分题解:凸包斜率判断,C++ 代码 20行内实现

📅 2026/7/13 7:43:00
PAT L3-009 长城 30分题解:凸包斜率判断,C++ 代码 20行内实现
PAT L3-009 长城几何问题的高效解法与C实现烽火台布局问题看似简单实则暗藏玄机。这道PAT甲级真题将几何直觉转化为精确算法考验着选手对数学概念的编程实现能力。想象你站在长城上如何用最少的烽火台覆盖整个北方防线这不仅是历史场景的再现更是算法思维的绝佳训练场。1. 问题本质与几何建模这道题的核心在于理解视线相切即监视的几何含义。给定一系列从南到北排列的顶点我们需要在这些顶点中选择最少的点建立烽火台使得北方任何位置与总部之间至少有一个烽火台的视线不被其他顶点阻挡。关键观察点烽火台只能建在顶点处监视方向仅向北输入序列的左侧视线相切也算有效监视总部位于最南端第一个顶点不参与计数这实际上是一个凸包问题的变种。我们需要找出那些在北部视野中形成凸点的顶点因为这些点会阻挡更南侧烽火台的视线范围。2. 算法选择与优化思路标准凸包算法通常使用Graham扫描或Andrew算法时间复杂度为O(nlogn)。但本题有特殊性质可以优化输入顺序优势顶点已经按从南到北排列相当于x坐标有序省去了排序步骤单方向检测只需考虑向北的视野简化了凸包计算斜率比较通过相邻点斜率关系判断凸性我们采用单调栈技术来实现O(n)时间复杂度的解决方案。基本思路是维护一个栈其中保存可能成为烽火台位置的顶点通过斜率比较剔除不必要的点。3. 核心算法实现细节让我们拆解算法关键步骤数据结构准备使用数组存储所有顶点坐标栈结构保存候选顶点索引标记数组记录哪些点被选为烽火台斜率比较函数bool check(int l, int mid, int r) { return (b[l]-b[r])*1.0/(a[l]-a[r]) (b[mid]-b[l])*1.0/(a[mid]-a[l]); }这个函数判断三个点是否形成凹形返回true或凸形返回false。当中间点相对于两侧点的斜率关系满足特定条件时说明中间点不会阻挡视野。主算法流程for(int i0;in;i) { if(tt) { // 栈不为空 while(tt1 check(i, st[tt], st[tt-1])) tt--; // 弹出非凸点 if(tt!1 !vis[st[tt]]) { // 发现有效凸点 vis[st[tt]] 1; ans; } } st[tt] i; // 当前点入栈 }关键操作解析while循环确保栈顶两点与当前点不形成凹形只有栈中第二个点之后的凸点才被计数排除总部使用vis数组避免重复计数4. 完整代码实现与注释以下是整合后的完整解决方案包含详细注释#includebits/stdc.h using namespace std; const int N 1e5 10; int a[N], b[N]; // 存储顶点坐标 int st[N], tt 0; // 单调栈及栈顶指针 bool vis[N]; // 标记是否已选为烽火台 int n, ans 0; // 顶点数和结果 // 检查三点是否形成凹形中间点可被移除 bool check(int l, int mid, int r) { // 比较斜率l-r的斜率 mid-l的斜率 return (b[l]-b[r])*1.0/(a[l]-a[r]) (b[mid]-b[l])*1.0/(a[mid]-a[l]); } int main() { cin n; for(int i 0; i n; i) cin a[i] b[i]; for(int i 0; i n; i) { if(tt) { // 栈不为空时处理 // 维护栈的凸性弹出导致凹形的点 while(tt 1 check(i, st[tt], st[tt-1])) tt--; // 栈顶元素是新的凸点排除总部 if(tt ! 1 !vis[st[tt]]) { vis[st[tt]] 1; ans; } } st[tt] i; // 当前点入栈 } cout ans; return 0; }代码亮点简洁高效仅20行核心逻辑时间复杂度O(n)空间优化仅使用几个数组空间复杂度O(n)边界处理通过tt!1排除总部计数避免重复vis数组确保每个点只计数一次5. 算法正确性证明与边界情况要理解这个算法的正确性我们需要分析几种典型情况基本情况三个连续顶点形成凸形 → 中间点必须建立烽火台三个连续顶点形成凹形 → 中间点可被忽略特殊情形处理共线点根据题意视线相切也算监视因此共线点不会影响结果单峰情况整个长城只有一个凸起只需在该顶点建烽火台锯齿形长城多个凸起需要分别建立烽火台复杂度分析每个顶点最多入栈和出栈一次因此while循环总次数不超过2n整体时间复杂度严格O(n)适合n≤1e5的约束6. 实际应用中的优化技巧在竞赛实践中可以进一步优化代码整数运算优化// 避免浮点运算改用交叉相乘比较 bool check(int l, int mid, int r) { return (b[l]-b[r])*(a[mid]-a[l]) (b[mid]-b[l])*(a[l]-a[r]); }输入输出加速ios::sync_with_stdio(false); cin.tie(0);空间压缩如果不需要记录具体哪些点被选中可以去掉vis数组直接在出栈时计数但要注意重复计数问题性能对比方法时间复杂度空间复杂度代码复杂度标准凸包O(nlogn)O(n)中等本文方法O(n)O(n)简单暴力检查O(n²)O(1)简单但不适用7. 同类问题扩展与变种思考这种基于单调栈的几何处理方法可以解决多种类似问题天际线问题寻找建筑物轮廓的关键点最大矩形问题在直方图中寻找面积最大的矩形视线障碍问题计算可见的建筑物数量变种思考如果烽火台可以建在任何位置不只是顶点如果监视方向是双向的南北都要覆盖如果每个烽火台有建设成本求最小总成本这些变种会引入更复杂的几何计算和算法设计但核心思路仍然离不开凸包和单调栈的应用。