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

解体思路:

吐槽:
题目对 007000.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;
	}
}
posted @ 2016-09-07 10:57  文之  阅读(1075)  评论(0编辑  收藏  举报