Input two nonnegative real numbers a and b, the maximum length of which is less than 400.
output the value a+b with the simplest form,i.e. without prefix 0, without postposition 0, omit decimal point if decimal part is 0.
翻译一下就是,400位的长浮点数加法。
输入两个最大长度小于400的非负实数a和b。用最简单的形式输出a+b的值,即。不带前缀0,不带后置0,小数部分为0则省略小数点。
数据结构:统一采用int数组进行储存,计算,在读取的时候就将char-48转化为对应的int值。
这种结构的优点在于计算过程简单,缺点是无法区分整数0还是初始化后的0,应对这种情况需要在函数中考虑有效数字的宽度,比如add函数,里面的参数就有一个len
400.
这种感觉。本题的核心思路类似于整数加法,但是对齐的点位不同,整数只需要把尾部直接对齐就行,所以处理很简单。本题也类似,只是有更多细节:
其中关于翻转,浮点数比较特别,浮点数要把浮点对齐(整数也可以理解为浮点在尾部的实数),这样的话就要考虑浮点后的位数(neg),在翻转的时候,neg比较小的要补上差的位,这样也就可以在反转的时候顺便对齐了。
a=123.45(pos=3,neg=2)
b=12.345(pos=2,neg=3)
翻转的时候,要额外给a补上1位(3-2=1),因此翻转后是下面这样,那个0就是额外补的。使用int数组的好处就是,你在翻转的时候,直接翻就行,如果用char形,补的时候还要把’\0’转化为’0’。
054321
54321
计算过程中,可能会出现进位,但是进位只可能进1位,所以用一个carry
变量来调整。
最后就是输出,输出的思路就是。
/*
@author cyy
*/
#include
#include
#define MAX_LEN 900 //粗略计算一下应该是801,a为400位的整数,b为400位的小数,考虑1进位,保险起见开900
int fltlen(int* num,int pos);//计算实数宽度
void read(int* num,int *pos);//读取实数,通过指针返回到pos里
void print(int* num,int pos);//打印实数
void reverse(int *pre,int* reverse,int end);//翻转实数
void add(int*a,int*b,int*c,int len);//累加
int main(void)
{
//freopen("input.txt","r",stdin);//乐学提交把这个注释掉
int a[MAX_LEN];
int b[MAX_LEN];
int a_rev[MAX_LEN];
int b_rev[MAX_LEN];
int c[MAX_LEN];
int c_rev[MAX_LEN];
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(a_rev,0,sizeof(a_rev));
memset(b_rev,0,sizeof(b_rev));
memset(c,0,sizeof(c));
memset(c_rev,0,sizeof(c_rev));
//读取,初始信息获取
int a_pos,b_pos;
read(a,&a_pos);
read(b,&b_pos);
int a_len=fltlen(a,a_pos);
int b_len=fltlen(b,b_pos);
int a_neg=a_len-a_pos;
int b_neg=b_len-b_pos;
int init_len=(a_pos>b_pos?a_pos:b_pos)+(a_neg>b_neg?a_neg:b_neg);//结果初始长度(不考虑进位)
int init_pos=a_pos>b_pos?a_pos:b_pos;//结果初始前缀长度(不考虑进位)
int carry=0;//结果是否产生进位
//printf("%d",init_pos);
//翻转
if(a_neg>b_neg)//1.234(3)>123.45(2)
{
reverse(a,a_rev,a_len-1);
reverse(b,b_rev,b_len-1+(a_neg-b_neg));
}
else
{
reverse(a,a_rev,a_len-1+(b_neg-a_neg));
reverse(b,b_rev,b_len-1);
}
//相加init_len位
add(a_rev,b_rev,c_rev,init_len);
if(c_rev[init_len]!=0)//查看是否有进位
carry=1;
//print(c_rev,-1);
//再次翻转(考虑进位)
reverse(c_rev,c,init_len+carry-1);
//输出(考虑进位)
print(c,init_pos+carry);
return 0;
}
//输入浮点数数组,返回有效数字长度,考虑0.xx,0,x
//思路:逆序扫描保证前导0,加pos保证整数部分的长度
int fltlen(int* num,int pos)
{
for(int i=MAX_LEN-1;i>0;i--)
{
if(num[i]!=0)
{
return (i+1)>pos?(i+1):pos;//取两者最大
}
}
//i=1还没return,说明就是x或者0,至少占据1位
return 1>pos?1:pos;//取两者最大
}
//读取一个浮点数,并且返回浮点正位置
void read(int* num,int *pos)
{
char ch;
int i=0;
*pos=0;
while((ch=getchar())!='\n')
{
if(ch=='.')//遇到浮点(TODO,考虑没有浮点的情况)
{
*pos=i;
}
else
{
num[i++]=(int)(ch-48);
}
}
if(*pos==0)//没有发现小数点,判定为整数,修正pos
{
*pos=i;
}
}
//输出一个浮点数,给定浮点位置,考虑0.xxx的情况和0的情况,pos<0(用-1)代表整数
void print(int* num,int pos)
{
int len=fltlen(num,pos);//代表有效数字长度
for(int i=0;i<len;i++)
{
if(i==pos)//如果pos<=len,代表整数,没机会打出.
{
printf(".");
}
printf("%d",num[i]);
}
printf("\n");
}
//从end开始,翻转
void reverse(int *pre,int* reverse,int end)
{
for(int i=0;i<=end;i++)
{
reverse[i]=pre[end-i];
}
}
//顺位相加,len位
void add(int*a,int*b,int*c,int len)
{
for(int i=0;i<len;i++)
{
c[i]+=(a[i]+b[i]);//叠加
c[i+1]+=c[i]/10;//进位
c[i]=c[i]%10;//本位
}
}
你们自己去查freopen函数吧,基操。
测试的时候,把要用的用例放到最前面就好
9.00009
34320
34329.00009 (中间有连续0的情况)
1.10
09.100
10.2 (假设输入不是标准形式)
999
1.11
1000.11(整数+小数)
300
200
500 (整数)
0.11
0
0.11 (0测试)
0
0
0 (0情况)
0.11
0.1
0.21 (前导0)
98.99
1.01
100(后导0 保证整数部分)
99.99
1.01
101
0.01
0.99
1 (后导0+取整)
99.89
1.01
100.9(后导0 1)
0.11
0.9
1.01(进位1+前导0)
99.9
1.12
101.02(普通1)
1.234
123.45
124.684(普通2)