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

written by Lyon(beginner)

原文地址:https://www.cnblogs.com/LyonLys/p/hdu_1063_Lyon.html