1054. 求平均值
本题的基本要求非常简单:给定N个实数,计算它们的平均值。但复杂的是有些输入数据可能是非法的。一个“合法”的输入是[-1000,1000]区间内的实数,并且最多精确到小数点后2位。当你计算平均值的时候,不能把那些非法的数据算在内。
输入格式:
输入第一行给出正整数N(<=100)。随后一行给出N个正整数,数字间以一个空格分隔。
输出格式:
对每个非法输入,在一行中输出“ERROR: X is not a legal number”,其中X是输入。最后在一行中输出结果:“The average of K numbers is Y”,其中K是合法输入的个数,Y是它们的平均值,精确到小数点后2位。如果平均值无法计算,则用“Undefined”替换Y。如果K为1,则输出“The average of 1 number is Y”。
输入样例1:
7
5 -3.2 aaa 9999 2.3.4 7.123 2.35
输出样例1:
ERROR: aaa is not a legal number
ERROR: 9999 is not a legal number
ERROR: 2.3.4 is not a legal number
ERROR: 7.123 is not a legal number
The average of 3 numbers is 1.38
输入样例2:
2
aaa -9999
输出样例2:
ERROR: aaa is not a legal number
ERROR: -9999 is not a legal number
The average of 0 numbers is Undefined
解体思路:
吐槽:
题目对007
、000.01
这种数据居然都是接受的....
题目对12.
这种数据也是接受的...
.9
也是接受的,不过测试点未包含这样的数据(本文的代码一不接受,代码二接受)
像这种对输出格式要求很高的题目,应该描述得更加清晰才是。
题目的格式控制说得很“宽泛”,实际上只有以下 3 点:
- 除了符号外,只能包含数字和小数点;
- 小数点只能有一位;
- 小数点后最多有两位。
本文包含两种解法:
代码一未使用 atof 函数,手动实现了类似功能。
代码二使用了 atof 函数。
代码一:
概述:
首先,字符串有效长度大于 8 的舍去(最大为 -1000.00 这种情况)。然后将符号(正负)单独录入。之后就只剩下数字和小数点,非这两种类型的字符可以直接 Pass 掉,退出循环,输出当前字符串。输入的过程中注意控制小数点的个数,然后进行字符串转 double 型的处理。
具体细节:
使用字符串来录入数据。题目中没有给定字符串长度,这里直接设定为 1000,如果提交不过,更改也很方便。
首先判断第一位是否是 '+' 或者 '-',若是,指针指向下一位(j=1)。如果为负,则置 flag_negative 为 -1,最后 sum 值乘以它就转换成了负数。
然后开始录入剩余的数字:
- 非数字和小数点,标记为无效,然后退出。
- 如果满足条件,开始转换字符串。这里使用的方法是,比如 123.45,先将 thisnum 计算至 12345,最后再除以 100 (由 dot 来统计),就可以得到其真正值。
- 再注意小数点的情况。如果之前未遇到数字或者有两个小数点,则标记为无效。(之前一开始就把类似 21. 这种小数点在末尾的数字当作无效数据,导致测试点 4 一直通不过。)
- 最后指针走到字符串末尾时,对 sum 和 count 进行统计。
代码二:
使用了 ctype.h 中的 isdigit 函数判断是否为数字字符,使用了 atof 函数将字符串转为 double 型数值。
atof 函数省去了转换为数值的部分,只要先筛选出来既符合 atof 函数格式又是有效字符的格式就可以。先了解一下 atof 函数的参数格式:
该函数包含在 <stdlib.h> 中。
它可以将一个字符串转换为双精度的浮点数。
输入一串具有特殊类型的字符串可以被解释为一个数值。该函数在遇到第一个无法作为数的一部分的字符时停止。这个字符也可能是字符串结束符。
其参数格式为:
[whitespace] [sign] [digits] [.digits] [ {d | D | e | E }[sign]digits]
- whitespace 可以是空格,可以是 Tab。
- sign 可以是 '+' 或者 '-'。
- digits 是一个或多个十进制字符。
- 如果小数点前没有出现数字,那么在小数点之后至少要有一个数字。
- 十进制数字中也可以包含一个指数,由一个引导字符 (d, D, e 或者 E) 加上一个可选的十进制数字构成。
atof 函数更加宽容,比如输入 12.34ab
,它会一直转换到自己不能转换的位置,所以它会输出 12.34
。
根据题目的要求,在对字符串转换前做出以下筛选即可:
- 除了符号外,只能包含数字和小数点;
- 小数点只能有一位;
- 小数点后最多有两位。
解体代码:
代码一:
#include<stdio.h>
#include<string.h>
#define MAX 1000
int IsNumber (int i);
int main()
{
int N;
scanf("%d", &N);
double sum = 0;
int count = 0;
for (int i=0; i<N; i++) {
char s[MAX];
scanf("%s", &s);
int flag_negative = 1, invalid = 0;
int len = strlen(s), j = 0;
if (len <= 8) {
if (s[0] == '-' || s[0] == '+') {
if (s[0] == '-') flag_negative = -1;
if (s[0] == '+') flag_negative = 1;
j = 1;
if (len == 1) {
invalid = 1;
} // 只有一个负号或正号的情况
}
int dot_count = 0; // 小数点个数
int thisnum = 0;
int dot = 1; // 用 1 10 100 这样的数来记录小数位数
int flag = 0; // 标记遇到数字
for (j; j<len; j++) {
if (!IsNumber(s[j]) && s[j]!='.') {
invalid = 1;
break;
} // 非数字 & 小数点,就退出
if (IsNumber(s[j]) && dot_count<2) {
thisnum = thisnum * 10 + s[j] - '0';
if (dot_count) {
dot *= 10;
if (dot == 1000) {
invalid = 1;
break;
} // 小数点后有 3 位为无效
}
flag = 1;
}
if (s[j] == '.') {
dot_count ++;
if (!flag || dot_count==2) {
invalid = 1;
break;
} // 之前没遇到数字/小数点有两个
}
if (j == len-1) {
double temp = (double)thisnum / (dot * flag_negative);
if (temp<=1000 && temp>=-1000) {
count ++;
sum += temp;
} else {
invalid = 1;
}
}
}
if (invalid) {
printf("ERROR: %s is not a legal number\n", s);
}
} else {
printf("ERROR: %s is not a legal number\n", s);
}
}
if (count) {
if (count == 1) {
printf("The average of 1 number is %.2f\n", sum/count);
} else {
printf("The average of %d numbers is %.2f\n", count, sum/count);
}
} else {
printf("The average of 0 numbers is Undefined\n");
}
return 0;
}
int IsNumber (int i) {
if (i>='0' && i<='9') {
return 1;
} else {
return 0;
}
}
代码二:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define MAX 1000
int IsValid (char *s);
int main()
{
int N;
scanf("%d", &N);
double sum = 0;
int count_valid = 0;
for (int i=0; i<N; i++) {
char s[MAX];
scanf("%s", &s);
if (IsValid(s)) {
double temp = atof(s);
if (temp>=-1000 && temp<=1000) {
sum += temp;
count_valid ++;
} else {
printf("ERROR: %s is not a legal number\n", s);
}
} else {
printf("ERROR: %s is not a legal number\n", s);
}
}
if (count_valid) {
if (count_valid == 1) {
printf("The average of 1 number is %.2f\n", sum/count_valid);
} else {
printf("The average of %d numbers is %.2f\n", count_valid, sum/count_valid);
}
} else {
printf("The average of 0 numbers is Undefined\n");
}
return 0;
}
int IsValid (char *s) {
int len = strlen(s); // 不要把 strlen 放在循环里,耗时(虽然不影响本题)
int count_dot = 0;
int after_dot = 0; // 统计小数点之后的数字
if (len <= 8) {
for (int j=0; j<len; j++) {
if ((s[j]=='+' || s[j]=='-') && j==0) {
continue;
} // 跳过正负号
if (!isdigit(s[j]) && s[j]!='.') {
return 0;
} // 非数字和小数点,返回 0
if (count_dot) {
after_dot ++;
if (after_dot == 3) {
return 0;
} // 小数点后有 3 位,返回 0
}
if (s[j]=='.') {
count_dot ++;
if (count_dot == 2) {
return 0;
} // 小数点等于 2 个时,返回 0
}
}
return 1;
} else {
return 0;
}
}