【题目描述】
输入一些整数,求出它们的最小值、最大值和平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。
【样例输入】
2 8 3 5 1 7 3 6
【样例输出】
1 8 4.375
【题目来源】
刘汝佳《算法竞赛入门经典 第2版》 例题2-5 数据统计
【解析】
本题的难点在于,输入整数的个数是不确定的。因为不知道会输入多少个整数,程序只能通过循环不断要求用户输入,直到用户发出“输入结束”信号。
循环输入的代码很简单,但是用户的“输入结束”信号是什么呢?程序怎么接收这个信号呢?
按我们以前的经验,都是输入后按Enter键后开始输出,比如求两个整数的和。
#include<stdio.h>
int main(){
int a,b;
scanf("%d %d", &a, &b);
printf("%d\n", a+b);
return 0;
}
输入两个整数后按Enter,程序就会输出结果,看起来Enter键就是“输入结束”信号。但是假如我们在输入第一个数后就直接按Enter呢,你发现程序并没有输出任何结果,它依然在等着你输入,由此可见Enter并不是“输入结束”信号。
Enter发出的是“提交”信号。输入一个数,按Enter相当于对scanf说:老哥,给你交1个数!
但scanf要的是两个数,回答:老弟,你只交了1个,还差1个,继续!
所以,当你欠scanf老哥数字债时,不管按多少个Enter都无法结束输入。
如果输入9 5 2 7后按Enter,就是对scanf说:老哥,给你交4个数。
scanf老哥一看要求满足了,也不贪心,只取前两个,后面赠送的忽略不计。
那用什么发出“输入结束”信号,之前的文章“用恋爱脑搞懂scanf的返回值”已有介绍,在Windows下,输入完毕后按Enter键,然后按Ctrl+Z键,再按Enter键,即可结束输入。在Linux下,输入完毕后按Ctrl+D键即可结束输入。
显然,Windows下“Ctrl+Z”就是“输入结束”信号,为什么输入这个信号后还要加上一个Enter呢?别忘了,Enter的作用是“提交”,它得把这个“输入结束”信号“提交”给程序。
注意使用这个“输入结束”信号有两个条件:
①“输入结束”信号只是对当前的scanf老哥发出的,并不是对整个程序发出,因而它只能结束当前的scanf输入。
②“输入结束”信号要正常发挥作用,必须是在当前scanf语句未输入任何数据前按Ctrl+Z。
换句话说,上面的代码要想使用Ctrl+Z结束输入,只能在程序运行后,在未输入任何数据时就先按Ctrl+Z,然后程序就会在没有输入任何数据的情况下结束输入。
所以,准确地说,Ctrl+Z发出的“输入结束”信号是没有任何输入的“输入结束”信号。
这么看这个Ctrl+Z根本没个鸟用。
事实并非如此,因为当Ctrl+Z在发出“输入结束”信号的同时,scanf()函数会返回EOF(实际值为-1),咱们可以利用这个返回值再借助于循环实现有数据输入的“输入结束”。
代码如下:
#include<stdio.h>
#define INF 1000000000
int main(){
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;
n++;
}
printf("%d %d %.3f\n", min, max, (double)s/n);
return 0;
}
当输入完数据后,按Enter键再按下Ctrl+Z,然后再按下Enter,就会向当前正在执行scanf函数发出“输入结束”信号,当前scanf函数在没有任何有效输入的情况下结束输入。此时scanf返回EOF,致使循环条件不成立,结束循环。
为什么输入数据后要先按下Enter键再按Ctrl+Z呢?因为这样才能满足上面说的第②个条件。
不过上述代码不先按Enter键也是能正常运行的,甚至在输入完数据后按任何一个字母都能输出正确结果。这是因为当输入的不是整数时,匹配失败,scanf函数会返回0,同样能结束循环。注意:此时Ctrl+Z只被当作字符串处理,而不会被scanf视为“输入结束”信号。