资讯动态

洛谷P5717三角形分类:整数平方比较避开浮点陷阱的几何判定

发布时间:2026/10/17 7:25:49 来源:尧图企业网站定制
洛谷P5717这道题题号背后的“深基3.习8”已经说明它是一道入门阶段绕不开的练习。三角形分类看起来是个简单的判断题但我第一次提交就在精度和边界上栽了跟头连续两次吃罚时。这篇文章围绕这道经典题完整复盘几何判定标准、整数平方优化、输出细节和测试用例设计希望能帮到正在刷深基系列、卡在“简单题”上的朋友。1. 题目拆解三角形分类背后的几何判定标准1.1 输入是什么输出又该如何排序无论你在哪个在线判题平台做这道题题面基本一致输入三个整数代表三条边的长度程序需要输出对应的三角形类型。常见要求是输出四种情况之一“不能构成三角形”“直角三角形”“锐角三角形”“钝角三角形”。极少数版本还会混入“等腰三角形”和“等边三角形”但我刷到的这一版只考锐角、直角、钝角和无法构成所以判定逻辑天然就分成两步。这两步的顺序特别重要一上来就踩坑的人不少。有些同学拿到 1 2 3 这种输入看到两边之和等于第三边下意识就进了“钝角三角形”的分支结果判定条件根本没先做“两边之和大于第三边”直接输出错误。正确的执行顺序必须是先检查三边能否构成三角形能构成再判断锐角、直角或钝角。1.2 为什么用最大边就能决定整个三角形的类型判断三角形内角形状最直观的方法是计算三个角的余弦值但这在编程里既繁琐又容易引入浮点误差。其实有一个特别简单的数学事实当三条边排序结束后最大边对应的角就是最大角。最大角如果大于 90°钝角那这个三角形必然是钝角三角形最大角如果小于 90°锐角那另外两个角也一定小于 90°整体就是锐角三角形最大角恰好等于 90°就是直角三角形。把三条边排序后假设最大边是 c另外两边是 a 和 b那么通过余弦定理的变形可以直接判断a² b² c²最大角大于 90°输出钝角三角形a² b² c²最大角等于 90°输出直角三角形a² b² c²最大角小于 90°输出锐角三角形。这里有个值得写进笔记的细节判断对象必须是最长边。如果你随便拿一条边去对比比如输入 5 5 6你用 5 当“c”去算会得到 5²6² 5²于是以为它是锐角三角形事实也确实是锐角三角形但这纯属“碰巧”。换一组数据 2 3 4若拿 3 当“c”得到 2²4² 3²输出锐角可正确答案应该是钝角三角形因为最长边是 44² 2²3²。所以排序是所有后续判断的前提绝对不能省。2. 代码实现整数平方比较避开浮点坑2.1 三变量排序的两种推荐写法既然核心判定依赖“最大边”那第一件事就是把三边排好。排序三只变量根本不需要写完整排序算法最简单的手动换位就能解决问题。C 里我习惯这样处理#include iostream #include algorithm using namespace std; int main() { int a, b, c; cin a b c; // 让 a 是最小值c 是最大值 if (a b) swap(a, b); if (b c) swap(b, c); if (a b) swap(a, b); if (a b c) { cout Not a triangle endl; return 0; } int sideSquareSum a * a b * b; int maxSideSquare c * c; if (sideSquareSum maxSideSquare) { cout Right triangle endl; } else if (sideSquareSum maxSideSquare) { cout Acute triangle endl; } else { cout Obtuse triangle endl; } return 0; }Python 因为有内置的sorted写起来更短但同样要先取得最大边a, b, c sorted(map(int, input().split())) if a b c: print(Not a triangle) else: s a * a b * b t c * c if s t: print(Right triangle) elif s t: print(Acute triangle) else: print(Obtuse triangle)注意中间那个判断当a b c时三线段退化成一条直线构不成封闭图形所以必须用小于等于而不是小于。2.2 为什么坚持用整数运算而不使用 sqrt这道题的输入是整数那么所有平方也都是整数根本不需要开根号。很多新手看到“三角形边长”就本能地想到sqrt或者计算角度比如把三条边转成浮点数再用余弦定理求一个角度的大小。这样做不仅代码冗余还容易踩入浮点陷阱。举一个我在网上看到的高频错误写法double angle acos((a*a b*b - c*c) / (2*a*b)); if (angle 90.0) // ...这里如果角度恰好接近 90°但浮点数存储有截断误差例如是 89.9999999判断就会失手。更离谱的是输入三个比较大的整数比如1000000000, 999999999, 1414213562这种伪直角浮点误差可能直接改变分类结论让题目从“正确”变成“WA”。整数方案最大的优势在于a * a在绝大多数测评环境下都在可接受范围内不会溢出如果边长达上亿那需要转long long但那是另一层问题了。直接用平方大小比较就彻底绕开了浮点表达带来的二义性。判断三角形形状时我推荐一个口诀“比平方不夹角。”意思是只比较maxSide²与a² b²的大小不计算任何角度不调用acos或sqrt。3. 排查复盘三个很容易踩中的边界错误3.1 漏掉排序直角样例全被误判成锐角这是我第一次提交时“贡献”的罚时。当时我对三边长做了a b c的判断但根本没有按大小重新排序直接就比较a² b²和c²。我用3 4 5这种递增数据测试时没问题可换一种输入顺序4 5 3错误立刻出现。问题根因很清晰c此时可不是最长边。我拿着 5² 和 3² 去比较c²反而成了最小的一项结果显示25 9于是判成锐角三角形。你猜正确答案是什么4 5 3是一个直角三角形最大边是 5应该走5² 3² 4²这条分支。这种错误之所以隐蔽是因为我们自测时总使用递增输入比如3 4 5。可测评机不一定会按顺序给你数据5 4 3、4 5 3都是完全合法的输入顺序。所以排序一定放在所有判定之前如果你在题解区看到一个非常短的代码大概率也会在这三行里做swap。我复盘自己的排查过程时第一步并没有看输出而是把所有用例统一打印出排序后的结果看看a, b, c是否已经满足“最小中间最大”的顺序。一旦发现异常问题就一目了然。3.2 用 float 比较直角出现“看似正确但全是坑”的判定第二个坑发生在同学 A 的代码里。他一开始用了浮点版本double a, b, c; if (a * a b * b c * c) // 直角本地用3 4 5输出正常。数据稍微变形比如0.3 0.4 0.5就出问题了。在 C 中0.1这类小数在二进制浮点里并不可精确表示0.3² 0.4²的实际结果可能不严格等于0.5²。原本直角三角形的判定变成锐角直接 WA。这道题目里输入是整数于是解决方案非常干净把输入定义成整型并在int层面比较平方。如果你面对的是浮点输入版本那就要用“绝对值相差小于某个极小阈值”的方式判断if (fabs(a * a b * b - c * c) 1e-9) { // 直角三角形 }但既然是整数输入我强烈建议你不要把简单问题复杂化。说实话我看到很多题解还在用float这完全没有必要。三步排序三个平方比较代码最少正确率还最高。3.3 输出字符串不一致WA 最常见也最冤枉第三个坑忘了在哪一次比赛复现时遇到的。当时代码逻辑完全正确但输出用的是Right而题目要求的是Right triangle结果平台返回错误。这类问题在大厂笔试、课程作业里很常见尤其是字符串拼写不一致或者多了一个空格排序错误。提交前务必做到两件事把题面要求的输出字符串原样复制验证结尾换行如果平台用英文输出分清Acute、Right、Obtuse、Not a triangle不要自创一个Sharp或Triangle not found。字符串问题最气人因为你的算法完全正确却因为多了一个空格被扣分。我的经验是哪怕代码写完也要先跑一遍样例再把样例输出和题目输出字符逐字节比对尤其是行尾有没有多余空格。4. 边界用例把每种结果都测一遍4.1 一份覆盖全分支的最小测试表不管自测还是写题解我都建议把分支用一张表整理出来每一条都要过一遍。以下是我做 P5717 这类三角形分类题目时常用的几组用例输入排序后判定逻辑预期输出1 1 11 1 11 1 11² 1² 1²锐角三角形3 4 53 4 53² 4² 5²直角三角形4 5 33 4 5与上一条等价直角三角形2 2 32 2 32² 2² 3²钝角三角形1 2 31 2 31 2 3不能构成三角形2 3 42 3 42² 3² 4²钝角三角形5 5 65 5 65² 5² 6²锐角三角形这张表的意义在于它精准覆盖了所有输出分支。你会发现“4 5 3”和“3 4 5”是同一组数据的不同输入顺序它专门用来验证排序逻辑。如果排序步骤缺失这一条用例绝对会让你原形毕露。还有一种容易被忽略但数学上真实存在的情况等边三角形。比如1 1 1它满足1² 1² 1²属于锐角三角形。很多人的直觉里总觉得等边三角形是“特殊分类”但在这道题的标准输出里它还是锐角三角形。所以表里第一行就该放这个用例。4.2 防御性处理0、负数与超大数测评数据一般不会给负数边长或 0编程题的输入范围通常也限制正整数。但作为习惯我仍然会思考0 0 0、-1 2 3这类非法输入。我的代码里如果走int且只判断a b c-1 2 3这种畸形数据可能导致不可控分支。大多数题目保证输入合法所以这不算硬性要求但手写一遍能帮助你思考边界条件的本质。如果关心精度还可以把边长类型统一改成long long。尤其是某些题目把边长上限提到10^9a * a最高可到10^18刚刚超越int的表示范围但long long还撑得住。如果你在本地用int测了一组很大的数据得到奇怪的比较结果优先级最高的排查项就是溢出而不是再去检查三角形定理。4.3 自测时的“随机对拍”技巧我还喜欢用一个小技巧写一个随机数生成器产生三角形的三边再配合一个用余弦定理实现的“标准答案”版本进行对拍。生成随机边长如果两个版本输出不一致就说明有隐蔽分支没覆盖到。这道入门题当然不需要这么重的手段但养成“构造用例”的习惯对后续做复杂题目非常有帮助。用随机对拍覆盖上百组数据比手打十条用例更高效。你可以开一个 Python 脚本随机生成 1 到 100 之间的三个整数分别调用两个版本的程序出现差异时停止。如果你现在正在起步阶段花一小时掌握这个基本功会在后面的排序、二分查找、图论题里收获巨大。5. 思路延伸从这一题能带走的东西5.1 把数学公式翻译成代码优先级是“稳定”而不是“花哨”很多人一开始写代码会有一种错觉用高级语法、复杂函数库才显得厉害。但这道三角形分类的题目告诉你最简单稳定的解法往往才是最好的解法。整数平方比较、三次swap排序这些写法读起来毫无门槛但它在任何编译器上都表现稳定没有浮点误差也没有边界歧义。真正值得学习的不是三角公式本身而是“把数学问题代数化再把代数问题数字化”的决策过程。看到三角形第一反应不该是画图而是把判断条件变成三元比较看到平方第一反应不该是sqrt而是比较平方本身。这是计算机编程和手算数学的重要区别。另外一个通用的经验尽可能避免用浮点保存“精确”结果。除非题目明确要求输出浮点数值否则凡是可以整型比较的条件都先考虑整型方案。这样做的代价最小收益最高。5.2 分支覆盖与自测驱动才是新手晋级的分水岭从深基3系列刷到更高难度题目时你会发现很多问题不是“会不会公式”而是“边界处理得够不够完整”。P5717 就是一次很好的训练四种输出分支两个前置判定再加上输入顺序的不确定性恰好是新手最容易忽略的坑。建议你每次写完题解都问自己三个问题我的判定是否依赖了输入顺序我有没有覆盖“不能构成三角形”之前的判定我的输出字符串是否和题面严格一致这三个问题在三角形分类题里的答案其实也是很多复杂题目中边界条件的缩影。把这道题做透不是记住“直角三角形输出 Right triangle”而是建立起一套自己的自查清单。我个人的习惯是每道 AC 的题都保留一张最小测试表。这张表里不仅有常见样例还要有反例和边界值。今天的三角形分类用到的是1 1 1、3 4 5、2 2 3这些以后你刷到需要判断闰年、判断素数、判断区间交集的题目也能沿用类似思路。数据规模、输入顺序、边界约束这三个维度才是真正的通用框架。如果你在本地跑代码时发现自己输出和样例一模一样但提交还是 WA建议立刻检查是否误用了浮点数比较平方或者是否漏掉了对输入顺序的处理。三角形分类这道题本质上就是让你练熟“先排序再分支”的基本功它一点都不高大上但非常管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑