非算法原因的时间超限&&输入&&输入优化&&编写头文件

本文介绍了C++中cin和scanf输入导致时间超限的问题,探讨了输入优化的重要性。通过实验对比,展示了cin的低效,并推荐使用scanf或getchar进行优化。还讲解了读入优化的原理,并给出了使用getchar实现的优化代码段。最后,讨论了如何将优化代码封装为头文件,以方便重复使用。

非算法原因的时间超限&&输入&&输入优化&&编写头文件

输入优化的引入&&优缺点介绍

在输入输出较多的题目中,C++的选手会莫名其妙地出现Time Limit Exceeded(即TLE,时间超限)的评测结果(本人也曾经有过),于是对比AC程序,发现除了输入输出使用的函数和格式以外,几乎一模一样。

百思不得其解之后,便疯狂地求助,但大家(由于都没有经历过)很多人(特别是Pascal的)都不知道是什么问题,包括AC的人。于是,那位可怜的同志只好求助于互联网。

查完以后,他捶胸顿足,气愤得不得了——cin,cout速度过慢。

编者也做了一个实验:100以内的n个数,以文件为例,cin和scanf的区别。

数据构造代码如下(n可修改):

#include <cstdio>
#include <cstdlib>
#include <ctime>
#define n 1000000
#define max_num 100
using namespace std;
int main(){
    freopen("data.in","w",stdout);
    int i,x;
    printf("%d\n",n);
    for(i = 1; i <= n; i ++){
        x = rand() % max_num;
        printf("%d\n",x);
    }
    fclose(stdout);
    return 0;
}

scanf代码:

#include <iostream>
#include <cstdio>
using namespace std;
int main(){
    freopen("data.in","r",stdin);
    int x,n,i;
    scanf("%d",&n);
    for(i = 1; i <= n; i ++)
        scanf("%d",&x);
    fclose(stdin);
    return 0;
}

cin代码:

#include <iostream>
#include <cstdio>
using namespace std;
int main(){
    freopen("data.in","r",stdin);
    int x,n,i;
    cin>>n;
    for(i = 1; i <= n; i ++)
        c
<think> 我们要求500以内的勾股弦数,即满足勾股定理的三个正整数a, b, c(满足a² + b² = c²),并且要求b>a,同时c≤500。 根据引用,我们可以采用三重循环(a, b, c)来遍历,但这样效率较低。我们可以优化为两重循环(a和b),然后计算c=sqrt(a²+b²),并判断c是否为整数且不超过500。 同时,为了减少循环次数,我们可以假设a<b(因为b>a),并且由于a²+b²=c²,所以c>a且c>b,因此b和c的循环范围可以优化。 具体步骤: 1. 由于b>a,所以a的范围可以从1到499,b的范围从a+1到500(注意b最大500)。 2. 计算c = sqrt(a²+b²)。如果c是整数(即c的平方等于a²+b²)且c≤500,则记录(a,b,c)。 3. 将结果写入文件。 注意:由于c可能不是整数,所以我们需要判断c是否为整数。我们可以用:c==(int)c 来判断,但浮点数比较有精度问题,所以我们可以用整数判断:即计算c²是否等于a²+b²(但这里c是浮点数取整后的整数?),因此更安全的做法是: - 计算c_sq = a*a + b*b; - 计算c = sqrt(c_sq); - 判断c是否为整数:可以用 fabs(c - round(c)) < 1e-6 然后取整,或者直接判断 c_sq是否是一个完全平方数(即平方根的整数部分的平方等于c_sq)。 但是我们也可以避免浮点数:计算c = (int)sqrt(c_sq),然后判断c*c是否等于c_sq,并且c<=500。 因此,我们可以这样写: 伪代码: for a from 1 to 499: for b from a+1 to 500: c_sq = a*a + b*b; if c_sq > 500*500: # 超过500,直接break内层循环(因为b再增大,c_sq会更大) break c = (int)sqrt(c_sq) if (c*c == c_sq && c<=500) { 输出(a, b, c) } 但是,这里有一个问题:sqrt(c_sq)可能是整数,但取整后可能不是整数?我们这样处理:先取整,然后判断平方是否相等。 另一种方法是:我们计算c_sq,然后判断是否存在整数c满足c²=c_sq,且c<=500。由于c_sq最大可能为500000(当a=500, b=500时,但实际上我们的b循环到500,但a最大499,所以c_sq最大为500²+499²=250000+249001=499001),所以c最大为707(超过500),因此我们在内层循环中一旦c_sq超过500的平方(250000)就可以break(因为后面b增大,c_sq会更大,且肯定超过500)。但注意,我们要求c<=500,所以当c_sq>250000时,就可以break了。 因此,优化:当a和b满足a²+b²>250000时,就可以跳出内层循环(因为b再增大,c_sq会更大),然后进入下一个a。 另外,为了减少计算平方根的开销,我们可以事先计算一个数组,标记500以内的完全平方数,然后判断c_sq是否在数组中(且小于等于250000)。但这样需要额外的空间,不过空间要求不大(250000个整数标记,约250KB),但也可以接受。或者我们可以用一个集合存储1到500的平方,然后判断c_sq是否在这个集合中。 但是考虑到500以内的平方数只有500个,我们可以用一个数组或者哈希表存储平方数对应的平方根。即: squareSet = { i*i : i for i in range(1,501) } 然后判断c_sq是否在squareSet中,如果在,则c=squareSet[c_sq](但这里我们不需要c的值,因为c_sq的开方就是c,但c就是平方根,而且我们要求c<=500,所以只要c_sq在squareSet中,且c_sq<=250000(其实我们要求c<=500,所以c_sq最大250000))。 但是注意:c_sq可能大于250000,但我们之前已经判断过,如果a*a+b*b>250000,我们就break。所以我们可以先判断是否大于250000,然后再判断是否是完全平方数。 两种方法都可以,但整数运算通常比浮点运算快,而且避免浮点精度问题。 这里我们采用整数运算:先判断c_sq是否大于250000,是则break;否则,判断c_sq是否是一个完全平方数(即是否存在整数c使得c*c=c_sq)。判断完全平方数的方法:用整数开方后平方是否等于原数。 我们采用:c = (int)sqrt(c_sq); 然后判断c*c==c_sq,并且c<=500(因为c_sq<=250000,所以c<=500,所以只需要判断c*c==c_sq即可)。 因此,程序如下(用C++): #include <iostream> #include <fstream> #include <cmath> using namespace std; int main() { ofstream fout("pythagorean.txt"); // 创建输出文件流 int max_c = 500; int max_c_sq = max_c * max_c; // 250000 for (int a = 1; a <= 499; a++) { for (int b = a+1; b <= 500; b++) { int c_sq = a*a + b*b; if (c_sq > max_c_sq) break; // 因为b再增加,c_sq会更大,所以跳出内层循环 int c = sqrt(c_sq); // 平方根取整 // 判断c*c是否等于c_sq,并且注意c不能超过500(但c_sq<=max_c_sq保证了c<=500) if (c * c == c_sq) { fout << a << " " << b << " " << c << endl; } } } fout.close(); return 0; } 但是注意:sqrt函数返回的是double,我们转换为int会截断小数部分,所以有可能c=sqrt(c_sq)取整后,c*c可能小于c_sq,所以我们也可以四舍五入: c = (int)(sqrt(c_sq)+0.5); 然后判断c*c==c_sq。 或者使用cmath的round函数。但这里为了简单,我们使用四舍五入。 因此,修改为: int c = (int)(sqrt(c_sq) + 0.5); if (c * c == c_sq) { ... } 但是,由于c_sq是完全平方数,所以开方应该是整数,取整后应该正好是整数。但浮点数可能有精度问题,所以加上0.5再取整可以保证正确性。 另外,我们还要考虑a和b的范围:a从1到499,b从a+1到500。这样就不会有重复(因为b>a,且我们固定了a<b),并且不会漏掉。 但是,这种方法比三重循环快很多,因为内层循环在c_sq>250000时就break,所以b不会循环到500(当a较大时,b只需要循环几次)。 我们也可以进一步优化:当a固定时,b从a+1开始,而随着b增大,c_sq=a²+b²也在增大。当b达到某个值使得c_sq>250000时,就break。所以内层循环次数不会太多。 接下来,我们按照这个思路写代码。 注意:在C++中,sqrt函数需要包含cmath头文件。 但是,我们还需要考虑a和b的平方和可能溢出吗?a和b最大500,所以c_sq最大为500*500+500*500=500000,而int一般能表示到2e9以上,所以不会溢出。 因此,我们可以用上面的代码。 另外,题目要求将结果写入文本文件,每行一个组合。 我们运行程序,检查输出文件。 但是,我们还要注意:勾股数可能有重复吗?由于我们要求b>a,并且a和b都是正整数,所以每个勾股数三元组(a,b,c)中a<b,且c是斜边,所以不会重复(除有相同的三元组,但不同的a,b不会产生相同的三元组,因为a<b且c由a,b唯一确定,所以不会重复)。 另外,我们是否遗漏了a和b交换的情况?题目要求b>a,所以我们只输出a<b的情况,因此不会重复(比如(3,4,5)和(4,3,5),我们只输出(3,4,5))。 所以,我们的程序可以这样写。 下面给出完整代码: </think>### 求500以内勾股弦数的实现方法 勾股弦数指满足勾股定理 $a^2 + b^2 = c^2$ 的正整数三元组 $(a, b, c)$,其中 $b > a$。以下是高效的实现方法: #### 优化算法思路 1. **减少循环次数**:使用双重循环(a 和 b)替代三重循环,通过 $c = \sqrt{a^2 + b^2}$ 直接计算斜边 2. **边界优化**: - $a$ 范围:$1 \leq a \leq 499$(保证 $b > a$) - $b$ 范围:$a+1 \leq b \leq 500$ - 当 $a^2 + b^2 > 500^2$ 时终止内层循环 3. **整数判断**:通过取整再平方验证 $c$ 是否为整数 #### C++ 实现代码 ```cpp #include <iostream> #include <fstream> #include <cmath> using namespace std; int main() { ofstream fout("pythagorean.txt"); // 创建输出文件 const int MAX = 500; const int MAX_SQ = MAX * MAX; // 250000 for (int a = 1; a < MAX; a++) { for (int b = a + 1; b <= MAX; b++) { int sum_sq = a * a + b * b; if (sum_sq > MAX_SQ) break; // 超过范围终止循环 int c = sqrt(sum_sq); // 计算平方根取整 if (c * c == sum_sq && c <= MAX) { // 验证整数解 fout << a << " " << b << " " << c << endl; } } } fout.close(); return 0; } ``` #### 算法特点 1. **时间复杂度**:$O(n^2)$,比三重循环 $O(n^3)$ 显著优化 - 实际运行中内层循环提前终止,平均效率更高 2. **关键优化点**: - 当 $a^2 + b^2 > 500^2$ 时立即 `break`(斜边必然超限) - 整数验证避免浮点精度问题:`c*c == a*a + b*b` 3. **输出格式**:每行一组数据,如: ``` 3 4 5 5 12 13 6 8 10 ``` #### 数学原理 - 勾股定理:$$ a^2 + b^2 = c^2 $$ - 边界条件:$$ c = \lfloor \sqrt{a^2 + b^2} \rfloor \leq 500 $$ - 关系约束:$$ b > a > 0 $$ 此方法可高效生成所有满足 $c \leq 500$ 且 $b > a$ 的勾股数组合[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值