资讯动态

洛谷P5717三角形分类题解:条件分支与排序预处理技巧

发布时间:2026/10/16 2:38:37 来源:尧图企业网站定制
刷洛谷入门题的时候很多人会在P5717这道“三角形分类”上栽跟头。题目本身不难但它的判定顺序、浮点数比较、以及隐藏的多种情况分类恰恰是新手最容易犯迷糊的地方。这篇就围绕这道题把从读题到AC的完整思路、代码实现和踩坑记录都捋一遍顺便聊聊这类“多分支分类”题目背后通用的解题套路。1. 题目到底在考什么不只是“if-else”那么简单1.1 读懂题目要求是第一步P5717题目的描述非常直白给定三条线段长度判断它们能否组成三角形。如果能再进一步判断这个三角形是锐角三角形、直角三角形还是钝角三角形同时还要输出它是否为等腰三角形或等边三角形。这里头第一个容易被忽略的点是输出有两个维度。第一维度是“按角分类”第二维度是“按边分类”。这两个维度不是互斥的而是一个三角尺可以同时属于多个类别。比如等边三角形既是锐角三角形又是等腰三角形输出的时候需要同时体现。很多初学者只输出一个类别自然就WA了。另外一个隐含考点是题目给出了三条边的顺序但它们并不一定满足“a b c”之类的排列。如果直接按输入顺序去判断角的大小关系就得写很多分支条件代码会非常啰嗦。所以这里需要一个预处理步骤排序。1.2 核心考点拆解这道题虽然挂着“入门”标签但实际上包含了几个十分关键的编程基础技能条件分支的综合运用不仅仅是简单的单层if而是多层嵌套、多条件并列、优先级排列。排序思维通过将三边排序把“判断最大边”和“比较平方和”的逻辑大大简化。这种“先排序再处理”的思路在后续很多算法题里都会反复用到。浮点数的比较三条边的长度可以是整数吗题目并没有明确说实际上洛谷的测试数据都是整数但作为严谨的解法需要考虑到边长可能不是整数的情况。用浮点数判断直角时直接比较“a² b² c²”常常会因为精度问题而失败必须使用误差范围判断。数学定理的应用构成三角形的充要条件是“任意两边之和大于第三边”而判断角类型用的是勾股定理及其推论最大边的平方等于另外两边的平方和是直角大于则是钝角小于则是锐角。这些点单独拎出来都不难但它们组合在一起就成了新手测评机前的一道坎。1.3 适合谁来刷这道题如果你刚学完C或Python的基本语法正在找一些“看起来简单但暗藏细节”的题目练手P5717非常合适。它能帮你巩固if-else嵌套、逻辑运算符的优先级、以及数据预处理的思想。同时对于那些准备参加入门级编程比赛的同学这种题也是必须拿满分的送分题因为它的分类逻辑非常考察细心程度。2. 解题思路的拆解与方案选型2.1 为什么一定要先排序拿到三条边我们最自然的想法是设三条边为a、b、c然后分类讨论谁是最大边。如果不排序我们需要写如果a是最大边比较a²与b²c²如果b是最大边比较b²与a²c²如果c是最大边比较c²与a²b²这样三个分支内容几乎一样代码重复严重还容易漏情况。更糟糕的是判断是否能构成三角形时如果abc成立同时还需要bca和acb三个条件都得列出来。如果先把三条边按非递减顺序排好比如从小到大为a、b、c那么“最大边”就是c。此时构成三角形的条件只需要一个a b c。为什么因为a和b已经是较小的两条边只要它们的和能大于最大的c那么其他任意两边之和也必然大于第三边bca和acb显然成立因为c已经是最大边。同理判断角类型时只需要比较c²与a²b²的大小关系就行了。一个排序把多种情况收敛为统一模式这就是“数据预处理”的魅力。2.2 三角形构成的判定顺序先判断能否构成三角形如果连三角形都不是直接输出“Not triangle”后面全都不要管。这个顺序必须放在最前面。不要先判断等腰、直角那些因为那些都是建立在“能构成三角形”的基础上。在这里我还想强调一个细节构成三角形的条件究竟是“a b c”还是“a b c”严格意义上等于时三边共线无法构成三角形。题目里说的三角形默认是“非退化”的即任意两边之和严格大于第三边。所以一定要用大于号不能加等号。这一点在输入数据边界值比如1 1 2时特别容易出错输出应该是Not triangle。2.3 角的分类勾股定理的推论排序后c是最长边。根据余弦定理或者勾股定理的扩展如果 a² b² c²则夹角C是90度三角形为直角三角形。如果 a² b² c²说明C大于90度三角形为钝角三角形。如果 a² b² c²说明C小于90度三角形为锐角三角形。这里的“等于”在整数运算下可以用“”但如果边长是浮点数就绝不能直接用等于。推荐使用误差判断绝对值小于某个极小值比如1e-6就视为相等。有些同学会问如果我已经知道c是最大边了为什么不用余弦定理直接算角度因为那样需要用到反三角函数不仅慢而且精度误差更大。用平方比较是最高效且足够精确的方法。2.4 边分类的优先级等腰与等边的坑按边分类时通常的逻辑是等边三角形三条边都相等。等腰三角形至少两条边相等。不等边三角形三条边都不相等。这里有个常见坑一个等边三角形同时也满足等腰三角形的定义。但是题目要求的输出顺序是“先角后边”而且边分类里等边和等腰都需要输出。也就是说如果输入是3 3 3输出应该是Acute triangle Isosceles triangle Equilateral triangle注意不能只输出Equilateral triangle还得有Isosceles triangle。反过来如果只输出Isosceles triangle而不输出Equilateral triangle也是错的。所以下面的分支结构要注意if (至少两条边相等) cout Isosceles triangle endl; if (三条边都相等) cout Equilateral triangle endl;两个if是并列关系而不是else if。很多同学把等腰和等边写成else if结果等边三角形只输出了等腰丢了等边。2.5 整体输出顺序题目要求的输出顺序是固定的先判断并输出Not triangle如果有三角形再依次判断输出是否是直角三角形、锐角三角形或钝角三角形这三个里面必须且只能输出一个。是否是等腰三角形、等边三角形这两个可能同时输出也可能都不输出注意等边一定等腰。在实际写代码时角类型的三个分支用else if串联边类型两个分支用两个独立if。这样可以保证顺序和逻辑都不乱。3. 代码实现与关键步骤详解3.1 C实现下面给出我常用的C实现并对每个关键部分做说明。#include iostream #include algorithm using namespace std; int main() { int a, b, c; cin a b c; // 将三边从小到大排序保证 c 是最大边 int sides[3] {a, b, c}; sort(sides, sides 3); a sides[0]; b sides[1]; c sides[2]; // 1. 判断是否能构成三角形 if (a b c) { cout Not triangle endl; return 0; } // 2. 按角分类 if (a * a b * b c * c) { cout Right triangle endl; } else if (a * a b * b c * c) { cout Obtuse triangle endl; } else { cout Acute triangle endl; } // 3. 按边分类 if (a b || b c) { cout Isosceles triangle endl; } if (a b b c) { cout Equilateral triangle endl; } return 0; }核心要点用数组加sort排序比手动交换三个变量更简洁也避免出错。当然如果有些同学还没学到数组也可以手动排序但数组方式更通用。排序后c一定是最大边。但要注意sort默认升序所以c就是最大值。直角判定直接用。因为本题数据都是整数所以没有问题。如果担心题目使用浮点可以改成abs(a*a b*b - c*c) 1e-6。输出英文句子注意首字母大写单词之间空格结尾有换行。一个单词拼错都会WA。边分类里两个if是独立的因为等边必然等腰。3.2 Python实现Python版本逻辑相同只是语法上的差异。a, b, c map(int, input().split()) # 排序 a, b, c sorted([a, b, c]) if a b c: print(Not triangle) exit() if a * a b * b c * c: print(Right triangle) elif a * a b * b c * c: print(Obtuse triangle) else: print(Acute triangle) if a b or b c: print(Isosceles triangle) if a b and b c: print(Equilateral triangle)注意Python的exit()可以提前结束程序或者在画三角形判断之后用if ... else包裹后面的语句但用exit()更清晰。不过提交洛谷时使用sys.exit()更规范exit()在某些环境下可能有额外输出但洛谷的Python环境里用exit()没问题。稳妥起见也可以把后面所有输出都放在一个不构成三角形的else分支里但那样嵌套层层太多可读性差。我比较推荐exit()。3.3 手动排序版不依赖sort函数如果还没学到排序函数也可以手写交换逻辑。三条边排序其实很简单先找出最大值再比较剩下两个。int x, y, z; cin x y z; if (x y) swap(x, y); if (y z) swap(y, z); if (x y) swap(x, y);三段交换后x y z。原理类似冒泡排序的简化版这也是一个不错的练习。但实际做题时直接用sort更高效毕竟排序不是本题的学习重点。3.4 参数与数据范围分析洛谷的P5717原题没有明确说明输入数据范围但从“深基3.习8”的定位推断三边长度是正整数且不大于1000。因此用int完全够用。平方之后最大是1000*10001,000,000三个平方和也不超过3,000,000int没问题。但有些变式题可能边长很大比如到10^9平方就超出int范围需要用long long。为了稳健我见过有人直接定义long long这样保险又不吃亏。如果你平时做题习惯用int也请记得在乘法面前多加一个long long转换。比如1LL * a * a ...防止意外溢出。虽然本题用不上但这是一个好习惯。3.5 一个易错点输出字符串的精确匹配洛谷的判题机对输出要求极其严格大小写、空格、换行都不能错。有些题目的提示里写了“Right triangle”而不是“right triangle”哪怕首字母大写错了都会判WA。还有“Isosceles”和“Equilateral”这两个单词一个拼错就会前功尽弃。我建议提交前把题面里的英文原样复制出来或者在自己代码里一个一个字母对一遍。不要靠记忆敲人的记忆对英文单词的末尾拼写很容易出错。4. 代码提交后的常见问题与排查实录4.1 为什么我的程序输出了“Isosceles triangle”但没有“Equilateral triangle”这是典型的把两个独立if写成了else if。当你输入3 3 3时满足a b所以第一个if输出等腰。接着是else if由于上一个if已经执行了编译器认为整个语句块结束不会再判断a b b c从而漏掉了等边输出。解决办法就是把第二个if前面的else去掉让它们并列。代码对比错误版本if (a b || b c) { cout Isosceles triangle endl; } else if (a b b c) { cout Equilateral triangle endl; }正确版本上面已经给过关键在于else的缺失。4.2 为什么我的“Not triangle”一直和后面的输出一起出现原因可能是你使用了多个if而没有互相排斥。比如你写了if (a b c) { cout Not triangle endl; } if (a b c) { // 继续判断... }这样其实也能工作但如果你忘了用else或者return程序在输出Not triangle后还会继续往下执行后面的判断导致多输出几行。最简单的修复方案是在输出Not triangle后直接return 0;或exit()提前终止程序后面的代码都不执行。这比加else包一层更直观。4.3 浮点数比较时为什么“a² b² c²”总是False如果你做的变式题输入是浮点数那么直接判等十有八九失败。比如1.0、1.0、1.41421356这组数据1.41421356的平方和2的误差可能在1e-8级别。这时候要用绝对误差判断#include cmath if (fabs(a * a b * b - c * c) 1e-6) { cout Right triangle endl; }注意:1e-6这个阈值的选取要合适。太小可能把精度误差算成不相等太大则会把本应是锐角或钝角的三角形误判为直角。通常1e-6在竞赛中可以接受。如果题目保证输入是整数直接用即可避免浮点误差的烦恼。4.4 为什么排序后a、b、c变了但后面判断之前没有更新如果你是用三个单独的变量而不是数组int a, b, c; cin a b c; // 排序 if (a b) { int t a; a b; b t; } if (b c) { int t b; b c; c t; } if (a b) { int t a; a b; b t; }这里的交换逻辑初学者容易写错。比如忘了第三个交换就会导致排序不完整。建议你使用数组加sort避免手写交换出幺蛾子。如果排序不对后面所有的判断都会基于错误的“最大边”结果自然全错。我调试这类题时最喜欢在排序后加一句cout a b c endl;来验证确认排序无误后再继续。你在本地调试时可以这样做但提交前记得删掉调试输出。4.5 等边三角形的角度分类到底是什么有些同学会困惑等边三角形三个角都是60度所以它属于锐角三角形吗是的60度小于90度所以它是锐角三角形。因此在角分类里等边三角形会走到最后的else分支输出“Acute triangle”。这道题里输入3 3 3预期的输出是Acute triangle Isosceles triangle Equilateral triangle如果少了任何一行就说明分支逻辑还有问题。4.6 用dev-C测试明明正确为什么提交洛谷是WA洛谷用的是Linux系统编译环境和本地Windows可能存在细微差别但更常见的原因是本地测试的用例太单一没有覆盖边界情况。比如你只测试了3 4 5、6 8 10、5 5 5却漏了1 1 2这种不能构成三角形的数据也漏了2 2 3这种等腰锐角三角形。建议把下面这些测试用例全部跑一遍输入预期输出1 1 1Acute triangle, Isosceles triangle, Equilateral triangle3 4 5Right triangle2 2 3Acute triangle, Isosceles triangle3 3 5Obtuse triangle, Isosceles triangle1 1 2Not triangle5 12 13Right triangle2 2 2Acute triangle, Isosceles triangle, Equilateral triangle1 2 3Not triangle4 4 7Obtuse triangle, Isosceles triangle6 8 10Right triangle这些用例基本覆盖了所有分支组合。批量测试是一个很好的做题习惯能让你的程序和单点思维彻底告别。4.7 为什么我的程序在顺序上先输出了“Isosceles”再输出了“Acute”题目要求的输出顺序是先角后边也就是如果同时是锐角三角形和等腰三角形应该先输出“Acute triangle”再输出“Isosceles triangle”。如果你的代码把边的判断放在角的判断前面输出顺序就反了会WA。这一点在扫描题目时很容易被忽略。一定要仔细看题面示例或者根据输出描述的顺序调整代码。我们的代码把角的判断写在前面边上两个if写在后面就是基于这个要求。5. 从这道题延伸到通用编程能力5.1 条件分支的排列艺术P5717是一个特别好的条件分支练习因为它的分类逻辑不是线性的而是先有一个前置条件能否构成然后有两个维度的分类角、边维度之间不完全独立。这种结构在现实业务代码中也很常见比如判断一个订单的状态是否有效既要判断状态码又要判断时间范围。学会用“提前返回”或“分层判断”来组织代码能让逻辑清晰很多。我的建议是遇到分类讨论题先画一个简单的逻辑树把互斥分支和并列分支画清楚。互斥分支用if-else if并列分支用多个if。就像这道题角分类三个分支互斥边分类两个分支有包含关系于是用一前一后交错实现。如果你能在草稿纸上把这个逻辑树画对代码基本不会错。5.2 排序预处理让问题简化一半这道题最终AC的关键其实是那个sort(sides, sides 3)。排序后“最大边”一目了然判断三角形构成的复杂度从三次比较降为一次比较。这种“先排序再处理”的模式在很多算法中都有体现在求中位数、众数时先排序。在贪心算法中经常先按某种规则排序比如活动安排、部分背包。在双指针算法中排序后可以移动左右指针来逼近目标值。所以在解题时如果发现需要反复比较几个数的大小不妨想想能不能先排序。多花几行代码换来的是大量重复逻辑的消除性价比极高。5.3 测试用例设计不要只测“正常情况”很多新手的代码“看起来对”但交上去就是WA根本原因是测试覆盖不全。P5717这个题我见过有人只测了直角三角形和等边三角形漏了“不能构成三角形”和“钝角等腰三角形”。设计测试用例时要尽量覆盖每一个分支每个输出字符串至少出现一次。边界值两边之和刚刚等于第三边。特殊形状等边、等腰直角比如1 1 根号2整型没有但可以构造45度等腰直角比如输入整数边长不行放弃。排列顺序输入顺序打乱比如5 3 4确认排序逻辑有效。把上面的测试用例表拿去跑一遍AC就是十拿九稳的事。5.4 从入门题到竞赛技巧多写几行防御性代码竞赛中有一个常见策略能不用浮点就不用浮点。这道题用整数平方和就能精确判断直角就不要去开根号、算角度。另外能用排序简化就不要手写一堆if。能提前return就绝不加大括号嵌套。这些小习惯能让你在更大规模的题目中减少出错概率。我还喜欢在写代码前先定义好每个输出字符串的常量或者直接在cout里写原样句子确保拼写正确。有的选手喜欢用宏定义字符串减少重复输入。但这里其实只有一次输出直接写就够了。6. 一些额外的想法与个人经验刷题刷多了以后你会发现很多“送分题”其实并不那么“送分”它们隐藏着一些值得反复咀嚼的设计。P5717就是一个典型它把几何数学和编程逻辑编排得恰到好处既不会让你觉得高不可攀也不是无脑输出结果。它考察的正是程序员的基本功——把现实世界的分类规则翻译成计算机语言。我自己的体验是第一次写这道题时因为没考虑“等腰和等边并列输出”反复WA了三次。当时我甚至怀疑题库有问题后来打印出中间结果才发现3 3 3的输入我的程序只输出两行。那一刻才真正理解“if和else if”的区别。这种东西光看书是学不会的必须亲手摔一跤。如果你现在正处于“看着题解能懂自己写却总是差一点”的状态我建议你先把我的代码自己敲一遍然后遮住代码重新从零写。写完之后用我给出的测试用例表逐一验证。这个过程可能花不了二十分钟但它能帮你真正掌握条件分支的精髓。千万不要直接复制粘贴提交那样你只是完成了一道题而没有收获一种思考方式。最后再分享一个小技巧以后遇到任何需要多条件分类的题都先在注释里把逻辑树写出来再对着注释写代码。比如这道题我会写// 排序 // 如果不能构成三角形 - Not triangle结束 // 按角分三类互斥 // 按边分两类并列把注释变成代码你的思路就会清晰得多。这个习惯我跟很多人推荐过实测非常管用。希望这篇拆解能帮你顺利AC这道题也让你对“分类讨论”这四个字有更深的理解。

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

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

免费获取报价 →
↑