hdu 1063 Exponentiation代码及分析
在看过我们学校前几届的大牛他的博客后,我决定也用Exponentiation来作为我的第一篇解题报告,不过我用的是hdu的Exponentiation。
http://acm.hdu.edu.cn/showproblem.php?pid=1063
之所以我用hdu的而不用poj 1001的题,是因为这个的技术含量相对较高。poj 1001的那题我依稀记得当时我用了半个小时AC了,但是我在hdu的acm step里面再次遇到它时却耗费了我将近两个小时来AC。其中一个原因是hdu出的数据太坑人了,明明说好的是含小数的100以内的数,但是在我WA后,我不断地修改,不断地参考别人的代码,最后才发现原来这里还有只是整数作为底数的数据。
这个题目的没在高精度乘法里让我们用分治法处理作限制,因而只要普通的高精度乘法就能够完成任务了。这题相对较难的是小数点位置的确定,我没有像网上那些大神那般的高精度计算,反而我是用了比较慢的处理方法,从而保证结果的正确。就是这样,我在数据读入后做预处理(pre_process)和数据输出前的收尾工作(post_process)。因为数据量不是很大,这就足以让我有更多的时间减少中间计算的失误。
下面就是我的代码,简单的说就是处理好数据立后就不断地调用高精度乘法,然后在数据输出前再次处理数据(加上小数点以及去掉没用的0)……
View Code
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 #include<math.h>
5
6 char base[10];//名称相对来说挺明确的了,应该不会有混乱的情况发生
7 char *ans;
8 int base_pow, point_pos;
9 int sign_z;//这个要说明一下,因为我的代码是基于poj1001重写出来的,因而一开始没有判断整数货小数的标识符,所以就加了这个是否Z的标识
10
11 void pre_process(void)//这是预处理,主要是用来判断输入的数是不是整数,以及观察小数点的位置,从而为后续工作做准备
12 {
13 int i;
14 for(i=0; i<6; i++)
15 if(base[i] == '.')break;//遇到小数点就停止,从而得出小数的位数
16 if(i>=6)sign_z=0;//如果找不到小数点,说明输入的是整数
17 else
18 {
19 sign_z=1;
20 point_pos = (5-i)*base_pow;
21 for(; i<5; i++)//这个是为了高精度乘法后处理的方便,把末尾的零都补上了
22 {
23 if(base[i+1])
24 base[i]=base[i+1];//从小数点之后的那位开始逐位移动
25 else
26 base[i]='0';
27 }
28 base[5]=0;//收尾,防止越界
29 }
30 }
31 char *multi(char *c_a, char *c_b)//高精度乘法,理论上可以移植到其他程序去
32 {
33 int i, j, k, l;
34 int len_a=strlen(c_a);
35 int len_b=strlen(c_b);
36
37 int *a = (int *)malloc(len_a*sizeof(int));
38 int *b = (int *)malloc(len_b*sizeof(int));
39 int *sum = (int *)malloc((len_a+len_b) * sizeof(int));
40 char *c_sum = (char *)malloc((len_a+len_b+2) * sizeof(char));//这里本应只需+1位,但为了添加小数点后输出的方便,那一位是留给小数点的
41
42 memset(a, 0, len_a*sizeof(int));//这一部分都是用于初始化的
43 memset(b, 0, len_b*sizeof(int));
44 memset(sum, 0, (len_a+len_b)*sizeof(int));
45
46 for(i=0; i<len_a; i++)a[i]=c_a[i]-'0';//转化字符数组为普通数组
47 for(i=0; i<len_b; i++)b[i]=c_b[i]-'0';
48
49 for(i=0; i<len_a; i++)//进行乘法运算,其实就是小学竖式乘法的原理无异
50 for(j=0; j<len_b; j++)
51 sum[i+j+1]+=a[i]*b[j];
52 c_sum[len_a+len_b]=c_sum[len_a+len_b+1]=0;//也是收尾用的
53 for(i=len_a+len_b-1; i>0; i--)//这是把大于9的部分进位,加到高位中去
54 {
55 sum[i-1]+=sum[i]/10;
56 c_sum[i]=sum[i]%10+'0';
57 }
58 c_sum[0]=sum[0]+'0';//转化回字符数组
59
60 free(a);//用完了就要扔,不应该囤积垃圾
61 free(b);
62 free(sum);
63
64 return c_sum;/*if(c_sum[0]!='0')return c_sum;else return &c_sum[1];*/
65 }
66 char *post_process(void)//这是收尾的处理,在这里把小数点加上去,以便输出时一并输出
67 {
68 int pre, post;
69 int len=strlen(ans);
70 int i;
71 if(sign_z){//如果有小数点的话……
72 for(i=0; i<point_pos; i++)//顺序寻找小数点应放的位置
73 ans[len-i]=ans[len-i-1];
74 ans[len-point_pos]='.';//加上小数点
75
76 post=len;
77
78 while(ans[post]=='0'||ans[post]=='.')//这是用来删除后缀0的
79 if(ans[post]=='.')
80 {
81 ans[post]=0;
82 break;
83 }
84 else ans[post--]=0;
85 }
86 pre=0;
87 while(ans[pre]=='0')pre++;//这是用来删除前缀0的
88
89 }
90 int main()
91 {
92 int i;
93
94 while(scanf("%s%d", base, &base_pow)!=EOF)//输入要求,做完那10题基本输入输出,我彻底的动了…………
95 {
96 pre_process();
97 ans=base;
98 for(i=1; i<base_pow; i++)//暴力的不断乘
99 ans=multi(ans, base);
100 printf("%s\n", post_process());//终于可以直接一次过输出了……
101 }
102 return 0;
103 }
104
105
106 End
2 #include<string.h>
3 #include<stdlib.h>
4 #include<math.h>
5
6 char base[10];//名称相对来说挺明确的了,应该不会有混乱的情况发生
7 char *ans;
8 int base_pow, point_pos;
9 int sign_z;//这个要说明一下,因为我的代码是基于poj1001重写出来的,因而一开始没有判断整数货小数的标识符,所以就加了这个是否Z的标识
10
11 void pre_process(void)//这是预处理,主要是用来判断输入的数是不是整数,以及观察小数点的位置,从而为后续工作做准备
12 {
13 int i;
14 for(i=0; i<6; i++)
15 if(base[i] == '.')break;//遇到小数点就停止,从而得出小数的位数
16 if(i>=6)sign_z=0;//如果找不到小数点,说明输入的是整数
17 else
18 {
19 sign_z=1;
20 point_pos = (5-i)*base_pow;
21 for(; i<5; i++)//这个是为了高精度乘法后处理的方便,把末尾的零都补上了
22 {
23 if(base[i+1])
24 base[i]=base[i+1];//从小数点之后的那位开始逐位移动
25 else
26 base[i]='0';
27 }
28 base[5]=0;//收尾,防止越界
29 }
30 }
31 char *multi(char *c_a, char *c_b)//高精度乘法,理论上可以移植到其他程序去
32 {
33 int i, j, k, l;
34 int len_a=strlen(c_a);
35 int len_b=strlen(c_b);
36
37 int *a = (int *)malloc(len_a*sizeof(int));
38 int *b = (int *)malloc(len_b*sizeof(int));
39 int *sum = (int *)malloc((len_a+len_b) * sizeof(int));
40 char *c_sum = (char *)malloc((len_a+len_b+2) * sizeof(char));//这里本应只需+1位,但为了添加小数点后输出的方便,那一位是留给小数点的
41
42 memset(a, 0, len_a*sizeof(int));//这一部分都是用于初始化的
43 memset(b, 0, len_b*sizeof(int));
44 memset(sum, 0, (len_a+len_b)*sizeof(int));
45
46 for(i=0; i<len_a; i++)a[i]=c_a[i]-'0';//转化字符数组为普通数组
47 for(i=0; i<len_b; i++)b[i]=c_b[i]-'0';
48
49 for(i=0; i<len_a; i++)//进行乘法运算,其实就是小学竖式乘法的原理无异
50 for(j=0; j<len_b; j++)
51 sum[i+j+1]+=a[i]*b[j];
52 c_sum[len_a+len_b]=c_sum[len_a+len_b+1]=0;//也是收尾用的
53 for(i=len_a+len_b-1; i>0; i--)//这是把大于9的部分进位,加到高位中去
54 {
55 sum[i-1]+=sum[i]/10;
56 c_sum[i]=sum[i]%10+'0';
57 }
58 c_sum[0]=sum[0]+'0';//转化回字符数组
59
60 free(a);//用完了就要扔,不应该囤积垃圾
61 free(b);
62 free(sum);
63
64 return c_sum;/*if(c_sum[0]!='0')return c_sum;else return &c_sum[1];*/
65 }
66 char *post_process(void)//这是收尾的处理,在这里把小数点加上去,以便输出时一并输出
67 {
68 int pre, post;
69 int len=strlen(ans);
70 int i;
71 if(sign_z){//如果有小数点的话……
72 for(i=0; i<point_pos; i++)//顺序寻找小数点应放的位置
73 ans[len-i]=ans[len-i-1];
74 ans[len-point_pos]='.';//加上小数点
75
76 post=len;
77
78 while(ans[post]=='0'||ans[post]=='.')//这是用来删除后缀0的
79 if(ans[post]=='.')
80 {
81 ans[post]=0;
82 break;
83 }
84 else ans[post--]=0;
85 }
86 pre=0;
87 while(ans[pre]=='0')pre++;//这是用来删除前缀0的
88
89 }
90 int main()
91 {
92 int i;
93
94 while(scanf("%s%d", base, &base_pow)!=EOF)//输入要求,做完那10题基本输入输出,我彻底的动了…………
95 {
96 pre_process();
97 ans=base;
98 for(i=1; i<base_pow; i++)//暴力的不断乘
99 ans=multi(ans, base);
100 printf("%s\n", post_process());//终于可以直接一次过输出了……
101 }
102 return 0;
103 }
104
105
106 End
written by Lyon(beginner)