#19271. 寻找勾股数
寻找勾股数
你好!我是你的OI金牌教练。
为了贴合初中数学背景并符合GESP 4级(涉及循环、分支、基本算法思维,通常数据范围在 到 之间,时间复杂度允许 或 )的难度,我为你设计了下面这道题目。
这道题考察了初中数学中著名的 “勾股定理” 以及编程中的 枚举算法与优化。
题目名称:寻找勾股数 (Pythagorean Triples)
题目描述
小杨最近在初中数学课上学习了“勾股定理”。老师告诉他,如果三个正整数 满足 ,那么这三个数就构成了一组“勾股数”,它们可以作为直角三角形的三条边长。
现在老师给出一个整数 ,请你编写程序帮助小杨找出,有多少组勾股数 ,使得由这三条边组成的直角三角形的周长不超过 。
为了避免重复计算,我们约定每组勾股数必须满足 。
输入格式
输入一行,包含一个正整数 。
输出格式
输出一行,一个整数,表示满足条件的勾股数有多少组。
输入输出样例 #1
输入:
30
输出:
3
样例 #1 解释: 周长不超过 30 的勾股数有以下 3 组:
- 。周长 ,满足条件。
- 。周长 ,满足条件。
- 。周长 ,满足条件。
注意:虽然 也是勾股数,但周长 ,不符合要求。
数据范围
对于 的数据,保证 。
题目分析与思路提示
作为教练,我来引导你如何在草稿纸上推导这道题。
1. 读题关键词
- “勾股数”:公式 。
- “周长不超过 ”:。
- “”:这大大减少了我们需要枚举的范围,且不用考虑去重。
- 数据范围 :这意味着 的算法(约 次运算)会超时,但 的算法(约 次运算)是完全可以接受的(通常 C++ 1秒能跑 次运算)。
2. 启发式思考过程(草稿纸推演)
思路一:暴力枚举(Naive Approach) 我们写三层循环枚举 ?
// 伪代码
ans = 0
for a from 1 to L:
for b from a+1 to L:
for c from b+1 to L:
if a*a + b*b == c*c AND a+b+c <= L:
ans++
- 复杂度分析:三层循环,接近 。当 时肯定超时 (TLE)。我们需要优化。
思路二:利用数学关系降维(GESP 4级标准解法) 其实我们不需要枚举 。 因为在直角三角形中, 是由 和 决定的:。 只要确定了 和 ,我们就算出了 ,然后检查两点:
- 算出来的 是整数吗?
- 是否小于等于 ?
-
草稿纸上画范围:
- 因为 且 ,最小的 是 3 (3-4-5三角形),最大呢?,所以 最多到 。
- 从 开始枚举,到多少呢?,所以 大约到 。
-
优化后的流程:
- 第一层循环枚举 (从 1 到 )。
- 第二层循环枚举 (从 开始)。
- 计算 。
- 判断 是否是完全平方数(即开根号后是整数)。
- 如果是,得到 ,判断 。
- 剪枝优化:如果在内层循环中发现 (因为 ,如果 都超了, 肯定超),就可以直接
break退出内层循环了。
3. 复杂度分析
- 时间复杂度:两层循环,。代入 ,运算次数约为 ,在 1 秒限制内非常安全。
- 空间复杂度:,只需要几个变量。
4. 代码实现细节(易错点)
- 判断完全平方数:可以使用
sqrt函数,但要注意浮点数误差。- 推荐写法:
int c = sqrt(a*a + b*b);然后判断if (c*c == a*a + b*b)。
- 推荐写法:
- 数据类型:,那么 最大约为 ,
int(最大 )完全存得下,不需要long long。
参考代码框架 (C++)
#include <iostream>
#include <cmath> // 需要用到 sqrt 函数
using namespace std;
int main() {
int L;
cin >> L;
int ans = 0;
// a 枚举范围:从 3 开始到 L/3
for (int a = 3; a <= L / 3; a++) {
// b 枚举范围:从 a+1 开始
// 剪枝条件:如果 a + b + (b+1) 已经超过 L,就没必要继续增大了
for (int b = a + 1; ; b++) {
// 这里可以做一个粗略的预判剪枝,c 肯定大于 b
if (a + b + b > L) break;
// 计算斜边的平方
int c_square = a * a + b * b;
// 计算 c
int c = sqrt(c_square);
// 检查 c 是否是整数 (即 c*c 是否等于原值)
if (c * c == c_square) {
// 检查周长
if (a + b + c <= L) {
ans++;
} else {
// 如果当前 a, b 算出来的 c 导致周长超了,
// 那么更大的 b 肯定也超,直接 break
break;
}
}
}
}
cout << ans << endl;
return 0;
}
快去试试把代码写出来并提交吧!这种结合数学知识的枚举题在 GESP 4级和 CSP-J 中都很常见。