2 条题解
-
0
这是一个功能完备的数据生成器。它集成了标准题解逻辑(用于生成
.out)和针对性数据构造逻辑(用于生成.in)。数据生成器功能说明
- 全自动生成:运行一次代码,即可在当前目录下生成
1.in/1.out到10.in/10.out共 20 个文件。 - 测试点覆盖策略:
- 测试点 1:。极小值边界,预期输出为 0(最小勾股周长为 ),测试程序是否能正确处理“无解”情况。
- 测试点 2:。恰好等于最小勾股周长,测试边界条件。
- 测试点 3:。题目样例数据,确保样例通过。
- 测试点 4-6:。中小规模数据,测试基础逻辑。
- 测试点 7-8:。大规模数据,测试程序在正常范围内的表现。
- 测试点 9:。接近最大值边界,奇数测试。
- 测试点 10:。题目允许的最大值,测试程序的效率上限。
C++ 数据生成器代码
/** * GESP 4级 [寻找勾股数] - 数据生成器 * 功能:自动生成 1.in/1.out ~ 10.in/10.out * 包含:标准解法逻辑 + 精心挑选的测试点 L 值 */ #include <iostream> #include <fstream> // 用于文件操作 #include <string> // 用于文件名生成 #include <cmath> // 用于 sqrt #include <vector> using namespace std; // ========================================== // Part 1: 标准题解逻辑 (用于生成 .out 文件) // ========================================== int solve(int L) { int ans = 0; // 枚举 a,上界约为 L/3 for (int a = 3; a * 3 < L; ++a) { // 枚举 b,从 a+1 开始 for (int b = a + 1; ; ++b) { long long c2 = 1LL * a * a + 1LL * b * b; int c = sqrt(c2); // 关键剪枝:如果当前周长已经超过 L,更大的 b 肯定也不行,退出内层循环 if (a + b + c > L) { break; } // 判定是否构成勾股数 if (1LL * c * c == c2) { ans++; } } } return ans; } // ========================================== // Part 2: 数据构造与文件生成 // ========================================== void generate_data() { // 预设 10 个测试点的 L 值,确保覆盖各类情况 int test_L[11] = { 0, // 占位符 10, // Case 1: 小于最小周长(12),结果应为 0 12, // Case 2: 恰好等于最小周长,结果应为 1 (3,4,5) 30, // Case 3: 题目样例 100, // Case 4: 小规模 250, // Case 5: 中小规模 500, // Case 6: 中等规模 1000, // Case 7: 大规模 1500, // Case 8: 较大规模 1999, // Case 9: 接近最大值 2000 // Case 10: 最大值边界 }; cout << "Start generating data..." << endl; for (int i = 1; i <= 10; ++i) { // 构造文件名 string in_name = to_string(i) + ".in"; string out_name = to_string(i) + ".out"; // 打开文件流 ofstream fin(in_name); ofstream fout(out_name); if (!fin.is_open() || !fout.is_open()) { cerr << "Error opening file for case " << i << endl; continue; } // 1. 写入输入文件 (.in) int L = test_L[i]; fin << L << endl; // 2. 运行标准算法并写入输出文件 (.out) int ans = solve(L); fout << ans << endl; // 关闭文件 fin.close(); fout.close(); cout << "Generated Case " << i << ": L = " << L << ", Ans = " << ans << endl; } cout << "All done! 20 files generated." << endl; } int main() { // 优化 C++ 标准流速度(虽然在此非必要,但在竞赛中是好习惯) ios::sync_with_stdio(false); cin.tie(NULL); generate_data(); return 0; }如何使用
- 将上述代码保存为
generator.cpp。 - 编译并运行:
g++ generator.cpp -o generator ./generator - 运行结束后,你的文件夹下会出现
1.in到10.in以及对应的1.out到10.out文件。 - 你可以将这些文件打包上传到 OJ(Online Judge)系统作为测试数据。
- 全自动生成:运行一次代码,即可在当前目录下生成
-
0
你好!作为OI金牌教练,我为你整理了这道 GESP 4级难度题目《寻找勾股数》的标准题解代码。
这份代码完全符合 NOIP C++14 竞赛标准,包含了详细的注释、复杂度分析以及针对更高阶选手的优化思考。
标准题解代码 (C++14)
/** * 题目:寻找勾股数 (Pythagorean Triples) * 难度:GESP 4级 / CSP-J 普及- * 算法:枚举 (Brute Force) + 剪枝优化 */ #include <iostream> #include <cmath> // 用于 sqrt 函数 #include <algorithm> using namespace std; // 建议开启 IO 优化,养成好习惯 void fast_io() { ios::sync_with_stdio(false); cin.tie(NULL); } int main() { fast_io(); int L; if (!(cin >> L)) return 0; int ans = 0; // 第一层循环:枚举边长 a // 优化1:确定 a 的上界 // 因为 a < b < c 且 a + b + c <= L // 所以 3a < a + b + c <= L => 3a < L => a < L/3 // 即使写 a <= L 也没错,但效率会低一点 for (int a = 3; a * 3 < L; ++a) { // 第二层循环:枚举边长 b // b 必须大于 a for (int b = a + 1; ; ++b) { // 易错点1:数据范围估算 // L <= 2000,平方和最大约为 2000*2000 = 4*10^6,远小于 int 上限 (2*10^9) // 所以这里用 int 足够,不需要 long long long long c2 = 1LL * a * a + 1LL * b * b; // 为了保险习惯性转 long long // 计算 c 的整数部分 int c = sqrt(c2); // 关键点2:剪枝 (Pruning) // 如果当前的 a, b 加上最小可能的 c (即 c > b) 已经超过 L, // 那么更大的 b 肯定也不符合条件,直接退出内层循环。 // 此时 c 还没验证是否是整数,但我们可以确定 c >= b+1 if (a + b + c > L) { break; } // 关键点3:判定完全平方数 // 浮点数比较有误差,最稳妥的方法是算出来整数 c 后,再平方回去看是否相等 if (1LL * c * c == c2) { // 此时满足 a^2 + b^2 = c^2 // 且因为是从小到大枚举,自然满足 a < b < c // 且前面剪枝保证了 a + b + c <= L ans++; } } } cout << ans << endl; return 0; }
详细分析与思考
1. 时间复杂度分析
- 思考过程:
- 如果我们使用三层循环枚举 ,复杂度是 。当 时,,远超 C++ 每秒约 次的运算极限,会超时 (TLE)。
- 我们根据 计算出 ,将三层循环降为两层循环。
- 外层循环 的范围大约是 。
- 内层循环 的范围受限于 。粗略估计 大约只能枚举到 左右。
- 结论:总的时间复杂度约为 。
- 当 时,计算量大约在 级别(几百万次),计算机可以在 10ms 内轻松完成。这完全符合 GESP 4级的时间要求。
2. 空间复杂度分析
- 分析:我们只使用了几个整型变量 (
L,a,b,c,ans等) 来存储数据,没有开辟数组。 - 结论:空间复杂度为 。
3. 关键点与易错点
- 浮点数判等:
- 错误写法:
if (sqrt(a*a+b*b) == (int)sqrt(a*a+b*b))。虽然在 较小时通常没问题,但这不是严谨的写法。 - 正确写法:先取整
c = sqrt(sum),再反算c*c == sum。这利用了整数运算的精确性。
- 错误写法:
- 循环退出条件 (剪枝):
- 内层循环
b没有写具体的终止条件(如b < L),而是利用if (a + b + c > L) break;。这是极其重要的一步。如果没有这个break,程序虽然逻辑正确,但会变成 的“慢版”,做很多无用功;如果数据范围扩大到 ,有剪枝的代码可能还能跑,没剪枝的就会慢很多。
- 内层循环
- 变量溢出:
- 虽然本题 不会溢出
int,但在处理类似题目(如 )时,a*a可能会爆int。竞赛好习惯是在计算平方和时强制转换为long long,如1LL * a * a。
- 虽然本题 不会溢出
4. 更优解法(拓展思考 - 适合 GESP 6级+)
如果数据范围加强到 , 也会超时。这时需要用到数论知识:欧几里得公式生成勾股数。
- 公式:
- 其中 ,且 ,且 一奇一偶(生成本原勾股数)。
- 所有勾股数都可以由本原勾股数乘以倍数 得到。
- 复杂度:通过枚举 ,复杂度可以降低到 甚至接近 。
- 注:对于 GESP 4级考生,掌握 解法已经满分。
- 思考过程:
- 1
信息
- ID
- 19271
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者