本文参考自:《算法竞赛入门经典(第2版)》(刘汝佳编),侵删。
例题2-5 数据统计
输入一些整数,求出他们的最小值、最大值和平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
-
在标准输入输出中,不知道循环次数和循环条件时:
添加
while(scanf("%d",&x)==1)
的语句,在Windows下,输入完毕后先按Enter,再按Ctrl+Z,再按Enter,即可结束输入。 -
上面的程序并不是很方便:每次测试都要手动输入许多数。尽管可以用前面讲的管道的方法,但数据只是保存在命令行中,仍然不够方便。
一个好的方法是用文件把输入数据保存在文件中,输出数据也保存在文件中。这样,只要事先把输入数据保存在文件中,就不必每次重新输入了;数据输出在文件中也避免了“输出太多,一卷屏前面的就看不见了”这样的尴尬,运行结束后,慢慢浏览输出文件即可。如果有标准答案文件,还可以进行文件比较1,而无须编程人员逐个检查输出是否正确。事实上,几乎所有算法竞赛的输入数据和标准答案都是保存在文件中的。
使用文件最简单的方法是使用输入输出重定向,只需在main函数的入口处加入以下两条语句:freopen("input.txt","r",stdin); freopen("output.txt","w",stdout);
比赛中一个普适的原则是:详细阅读比赛规定,并严格遵守。例如:输入输出文件名和程序名往往都有着严格规定,不要弄错大小写,不要拼错文件名,不要使用绝对路径或相对路径。
-
有一种方法可以在本机测试时用文件重定向,但一旦提交到比赛,就自动“删除”重定向语句。代码如下:
#define LOCAL #include<stdio.h> #define INF 10000000000 int main() { #ifdef LOCAL freopen("data.in","r",stdin); freopen("data.out","w",stdout); #endif int x,n=0,min=INF,max=-INF,s=0; while(scanf("%d",&x)==1) { s+=x; if(x<min)min=x; if(x>max)max=x; /* printf("x=%d,min=%d,max=%d\n",x,min,max); */ n++; } printf("%d %d %.3f\n",min,max,(double)s/n); return 0; }
这是一份典型的比赛代码,包含了几个特殊之处:
- 重定向的部分被写在了#ifdef和#endif中。其含义是:只有定义了符号LOCAL,才编译两条 freopen语句。
- 输出中间结果的 printf写在了注释中——它在最后版本的程序中不应该出现,但是又舍不得删除它(万一发现了新的bug,需要再次用它输出中间信息)。将其注释的好处是:一旦需要时,把注释符去掉即可。
上面的代码在程序首部就定义了符号 LOCAL,因此在本机测试时使用重定向方式读写文件。如果比赛要求读写标准输入输出,只需在提交之前删除
#define LOCAL
即可。一个更好的方法是在编译选项而不是程序里定义这个 LOCAL符号,这样,提交之前不需要修改程序,进一步降低了出错的可能。 -
如果比赛要求用文件输入输出,但禁止用重定向的方式,程序如下:
#include<stdio.h>
#define INF 1000000000
int main()
{
FILE *fin,*fout;
fin=fopen("data.in","rb");
fout=fopen("data.out","wb");
int x,n=0,min=INF,max=-INF,s=0;
while(fscanf("%d",&x)==1)
{
s+=x;
if(x<min)min=x;
if(x>max)max=x;
n++;
}
fprintf(fout,"%d %d %.3f\n",min,max,(double)s/n);
fclose(fin);
fclose(fout);
return 0;
}
重定向和 fopen两种方法各有优劣。重定向的方法写起来简单、自然,但是不能同时读写文件和标准输入输出; fopen的写法稍显繁琐但是灵活性比较大(例如,可以反复打开并其读写文件)。顺便说一句,如果想把fopen版的程序改成读写标准输入输出,只需赋值"fin=stdin; fout=stdout;"即可,不用调用fopen和fclose。
在Windows中可以使用fc命令,在Linux上可以使用diff命令。 ↩︎