算法竞赛中的输入输出框架
例题:输入一些整数,求出它们的最小值、最大值和平均值(保留三位小数)。输入保证这些数都是不超过1000的整数。
这个题目关键在于:整数的个数是不确定的。下面直接给出程序:
#include<stdio.h>
int main(){
int x,n=0,min,max,s=0;
while(scanf("%d",&x)==1){
s=s+x;
if(x>max){
max=x;
}
if(x<min){
min=x;
}
n++;
}
printf("%d %d %.3f\n",min,max,(double)s/n);
return 0;
}
这个程序中scanf有返回值,返回的是成功输入的变量个数,当输入结束时,scanf函数无法再次读取x,将返回0. 在Windows下,输入完毕后先按Enter键,再按Ctrl+Z键,最后在按Enter键即可结束输入。在Linux下,输入完毕后按Ctrl+D键即可结束输入。
上面这个程序是错误的,变量在未赋值之前的值是不确定的。特别的,它不一定是0.只需要在使用之前赋初值就可以解决这个问题。一种方法是定义一个很大的常数,如INF=1000000000,然后让max=-INF,而min=INF,另一种方法是先读取第一个整数x,然后令max=min=x.这样的好处是避免了人为的“假想无穷大”值,程序更加优美。
上面的程序并不是很方便,每次都要手动输入很多数。一个好的方法是用文件--把输入数据保存在文件中,输出数据也保存在文件中。这样,只要事先把输入数据保存在文件中,就不必每次重新输入了;数据输出在文件中也避免了“输出太多,一卷屏前面的就看不见了”这样的尴尬,运行结束后,慢慢浏览输出文件即可。
使用文件最简单的方法是使用输入输出重定向,只需在main函数的入口处加入以下两条语句:
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
上述语句将使得scanf从文件input.txt读入,printf写入文件output.txt。不只是scanf和printf,所有读键盘输入、写屏幕输出的函数都将改用文件。尽管这样做很方便,但并不是所有算法竞赛都允许程序读写文件。甚至有的算法竞赛允许访问文件,但不允许用freopen这样的重定向方式读写文件。参赛之前仔细阅读文件读写的相关规定:是标准输入输出(也称标准I/O,即直接读键盘,写屏幕),还是文件输入输出?如果是文件输入输出,是否禁止用重定向方式访问文件?同时注意不要弄错大小写,不要弄错文件名,不要使用绝对路径或相对路径。
有一种方法可以在本机测试时用文件重定向,但一旦提交比赛,就自动“删除”重定向语句。
#define LOCAL
#include<stdio.h>
#define INF 1000000000
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=s+x;
if(x>max){
max=x;
}
if(x<min){
min=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;
}
这是一份典型的比赛代码,包含了几个特别之处:
1.重定向的部分被写在了#ifdef和#endif中。其含义是:只有定义了符号LOCAL,才编译两条freopen语句。
2.输出中间结果的printf语句写在了注释中,它在最后版本的程序中不应该出现,可以在发现了bug后,输出中间信息。
上面的代码在程序首部就定义了符号LOCAL,因此在本机测试时使用重定向方式读写文件。如果比赛要求读写标准输入输出,只需要在提交之前删除#define 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,max,s=0;
min=INF;
max=-INF;
while(fscanf(fin,"%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;
}
上面这个代码很直观,先声明变量fin和fout,把scanf改为fscanf,第一个参数为fin;把printf改为fprintf,第一个参数为fout,最后执行fclose,关闭两个文件。
重定向和fopen两种方法各有优劣,重定向的方法写起来简单,但是不能同时读写文件和标准输入输出;fopen的写法稍显繁琐,但是灵活性比较大(例如,可以反复打开并读写文件)。如果想把fopen版的程序改成读写标准输入输出,只需赋值“fin=stdin;fout=stdout"即可,不要调用fopen和fclose。