Hello大家好我小白时隔很长时间又回来了~最近要准备考研了,白天看看数学专业课,晚上实在不想翻书的时候就会不自觉的去OJ做几道题——毕竟,日后复试有机考,手感还是非常重要的……当然了肯定是一些水题,毕竟作为一个菜鸡难题肯定是需要经验积累的……以及求助时肯定免不了各路大神的实力嘲讽……还好在下别的不行就是脸皮厚233333……好了废话不多说了言归正传
昨天去OJ上做了这样一道题:
链接:http://code.bupt.edu.cn/problem/p/439/
嘛这种题肯定是少不了一些废话的……提炼出来大致意思如下:
输入正方形边长a(0<a<=10001),a为实数,输出1、2、3三部分的面积,保留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吧……欢迎大牛们前来嘲讽与赐教……