那些曾经很不注意的detail(一)——输入那些事

Hello大家好我小白时隔很长时间又回来了~最近要准备考研了,白天看看数学专业课,晚上实在不想翻书的时候就会不自觉的去OJ做几道题——毕竟,日后复试有机考,手感还是非常重要的……当然了肯定是一些水题,毕竟作为一个菜鸡难题肯定是需要经验积累的……以及求助时肯定免不了各路大神的实力嘲讽……还好在下别的不行就是脸皮厚233333……好了废话不多说了言归正传

昨天去OJ上做了这样一道题:

链接:http://code.bupt.edu.cn/problem/p/439/

嘛这种题肯定是少不了一些废话的……提炼出来大致意思如下:

                                                                                               

输入正方形边长a(0<a<=10001)a为实数,输出123三部分的面积,保留6位小数

乍一看,哇,水题啊!这种题要是数学题显然一分钟就足够了(我应该不是在吹牛,恩)……于是非常愉快地提交了如下代码:

#include <bits/stdc++.h>
#include <cmath>
#define pi acos(-1)
#define m sqrt(3)
using namespace std;
int main()
{
    float a;
    while(scanf("%f",&a))
    {
        float S = a * a;
        printf("%f %f %f\n",(pi / 3.0 + 1.0 - m) * S,(pi / 3.0 - 4.0 + 2.0 * m) * S,(- pi / 1.5 + 4.0 - m) * S);
    }
    return 0;
}

非常悲催的是……TLE了……

tm是在逗我?算三个浮点数也能超时……冲这个计算效率CPU可以砸了重换了好吧……

秉持着OJ一定是对的”的观点,我愈发坚定绝对不是浮点数运算的问题……于是发到了论坛请教大佬……

毫无意外地被实力嘲讽了一波……当然了脸皮厚的人不在乎这些hhhhhhh

重点是……最后终于找到了问题所在:

while(scanf("%d",&n))

当时大牛的建议是这样的:

while(scanf("%d",&n) != EOF)

我试了一下……果然不再TLE了,变成了WA……这个就是下一回的事了……

我觉得很奇怪,一定是我的理解什么地方出了问题

后来经过对scanf()函数的浅显研究终于搞懂了……

翻一下stdio.h,我们可以找到scanf()的声明:

_CRTIMP int __cdecl __MINGW_NOTHROW	scanf (const char*, ...);

又查了一些百科,得到的结论是这样的:

int scanf("<格式化字符串>",<地址表>);

而函数的返回值是成功读取的变量个数

为了进一步搞懂,做了个小实验:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    int s = scanf("%d",&n);
    while(s)
    {
        printf("%d\n\n",s);
        s = scanf("%d",&n);
    }
    return 0;
}

而实验结果是这样的:

                                                                              

这说明,scanf()的返回值有三种:

1.输入无误的情况,此时返回成功读取的变量个数

2.输入有误的情况,比如我们要读一个整型变量,你非给我输入一个字母,此时scanf()函数不会对变量进行处理,并返回0(因为没有成功读取的变量)

3.EOF,文件结束符,在控制台表示输入结束,windows下用ctrl+Z表示,Linux下用ctrl+D表示,此时scanf()检测到输入结束,返回-1

这么说的话,问题就清楚了:

上面的代码中,当输入ctrl+Z时,scanf()返回-1,而-1作为非0值仍表示为真,故而仍然满足循环条件,只有输入不符合格式的数据才会跳出循环,而我们知道OJ上几乎不存在不合法的输入(至少我没见过),因而上面的循环条件就等价于死循环了,自然TLE……

而加入!= EOF之后,当用户输入ctrl+Z表示输入结束之后,就会跳出循环了……

可气的是以前没加过EOF从来没报过错……不然怎么可能到今天才注意!(好吧真像是在找借口……不过之前确实没报过错……)

果然过去基本不琢磨原理真的很伤……

----------------------------------------------------------------------------------------------------------------------------------------------------------

说到输入与TLE,我们就不得不讨论一下下面的问题了……

我们知道,通常C语言的标准输入函数是scanf(),C++采用输入流的方式,以cin>>的方式实现输入

就两者的区别来看,很多OJ中的FAQ都会说,当数据量大时scanf的速度明显快于cin,原理估计大部分打过ACM的大佬也都是清楚的:

scanf函数是格式化输入,实时,而cin需要先将字符流存入缓冲区再读取。当数据量一大,结果就可想而知了

C++不服啊!凭什么说我们IO效率低啊!

于是怀着各种不服又去百度了一下,结果发现,加入下面一行代码cin效率就和scanf()差不多了:

std::ios::sync_with_stdio(false);
唔……字面上理解好像是这样:是否与标准输入输出流同步,设置为否……

通俗点说,要不要将他们绑在一起,我们的答案是不要……

好像很有道理的样子啊!!要是不和stdio绑定就不用再存了啊!!(好吧这只是个人理解……有问题尽管留言嘲讽谢谢)

然而毕竟“实践是检验真理的唯一标准”,测一下吧(文件中为100000个两位随机正整数)

//scanf:
#include <stdio.h>
#include <time.h>
int main()
{
    int random;
    freopen("test.txt","r",stdin);
    double time;
    clock_t start,finish;
    start = clock();
    for(int i = 0 ; i < 100000 ; i++)
    scanf("%d",&random);
    finish = clock();
    time = (double)(finish - start)/ CLOCKS_PER_SEC;
    printf("Cost %lf s\n",time);
    return 0;
}
结果0.07s

//cin:
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
int main()
{
    int random;
    freopen("test.txt","r",stdin);
    double time;
    clock_t start,finish;
    start = clock();
    for(int i = 0 ; i < 100000 ; i++)
    cin>>random;
    finish = clock();
    time = (double)(finish - start)/ CLOCKS_PER_SEC;
    printf("Cost %lf s\n",time);
    return 0;
}
结果0.23s
3倍啊有木有!!!

加上std::ios::sync_with_stdio(false)之后……

0.08s……差不多了……

再也不用担心所谓的C++IO效率低的问题了!

----------------------------------------------------------------------------------------------------------------------------------------------------------

话说大家还记得之前我们说过那道题变成了WA吗……经研究,果然是差在了精度上……预知后事如何,且听下回分解……


ps:研究的估计都是一些各位早就耳熟能详的事情了……权当写给自己的note吧……欢迎大牛们前来嘲讽与赐教……




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值