poj 1001 Exponentiation(高精度运算)

来源:互联网 发布:企业网络信息安全培训 编辑:程序博客网 时间:2024/05/22 13:18
Exponentiation
博客转载自:http://blog.csdn.net/shaw1994/article/details/12273987
原题链接:http://poj.org/problem?id=1001
Problems involving the computation of exact values of very large magnitude and precision are common. For example, the computation of the national debt is a taxing experience for many computer systems. 

This problem requires that you write a program to compute the exact value of R nwhere R is a real number ( 0.0 < R < 99.999 ) and n is an integer such that 0 < n <= 25.
Input
The input will consist of a set of pairs of values for R and n. The R value will occupy columns 1 through 6, and the n value will be in columns 8 and 9.
Output
The output will consist of one line for each line of input giving the exact value of R^n. Leading zeros should be suppressed in the output. Insignificant trailing zeros must not be printed. Don't print the decimal point if the result is an integer.
Sample Input
95.123 120.4321 205.1234 156.7592  998.999 101.0100 12
Sample Output
548815620517731830194541.899025343415715973535967221869852721.0000000514855464107695612199451127676715483848176020072635120383542976301346240143992025569.92857370126648804114665499331870370751166629547672049395302429448126.76412102161816443020690903717327667290429072743629540498.1075960194566517745610440100011.126825030131969720661201
Hint
If you don't know how to determine wheather encounted the end of input: 
s is a string and n is an integer 
C++while(cin>>s>>n){...}cwhile(scanf("%s%d",s,&n)==2) //to  see if the scanf read in as many items as you want/*while(scanf(%s%d",s,&n)!=EOF) //this also work    */{...}
思路:

这道题目考查高精度乘法的计算。

要解决这道题目并不能使用cmath里的pow函数,而必须自己模拟乘法的计算过程,以实现高精度结果的输出。

所幸输入的底数一定是5位数,这给我们创造了一点方便。

我们可以将底数以字符串的形式读入,然后计算出结果中小数点应该在多少位,比如第一个结果应该是3*12=36。

关键的部分在于数据的处理,这里我的办法是倒序去掉小数点逐位存储,比如95.123,最后应该是用一个大小为5的数组存储为[3][2][1][5][9],每个单元只存一个数位。这里涉及到存储类型的问题,为了计算方便,最好用short类型的数组。

存储结果的数组需要开到150,我没有精确计算,大概是这么大,我选择160。

为了计算方便,我们不把数字多余的0去掉,而是在最后判断小数点的位置,然后输出该输出的数位。

每次乘法,都是用题目给的r乘上你上一次得出的结果,如果是第一次计算,那么就是r,计算的结果直接加到一个初始化为0的160大小的数组里面,注意是加。乘法的过程很容易模拟出来,就是让乘数让乘数第一位把你的结果的所有位乘一遍,然后再让第二位乘,注意加进数组时,有一个偏移量,乘法竖式都是这么写的,大家体会下。

为什么使用倒序存储的数组呢,想想上面对竖式的模拟就很容易明白了,从0位开始乘,结果也从0位开始存,非常方便。这样得出的结果也是倒序的。

输出结果时要小心,因为数组中存储的数位是倒序的,且没有小数点,需要计算从两边向中间的小数点方向第一个不为0数的位置,这是为了避免输出不必要的0而导致WA。

给出一个计算过程演示:

比如输入为1.0002 3

   20001

× 2

------------

  40002

此时结果数组中存储的是40002

   20001

×    0

------------

  40002

     00000

此时结果数组中存储的是40002

中间省略两步

   20001

×          1

 ------------

   40002

     00000

       00000

         00000

            20001

此时结果数组中存储的是400040001

   400040001

× 2

------------------

这里我就不计算了,最后结果数组里面存的是8000210060001

根据4*3=12,可以知道小数点应插在12号位,也就是11号位后面

800021006000.1

两边向中间,第一个不为0的数恰好就是两边,输出为

1.000600120008

代码:

#include<iostream>#include<string>#include<string.h>using namespace std;int main(){    string r;    int n;    const int len=160;    int result[len],a[len],b[6];    while(cin>>r>>n)    {        memset(a,0,sizeof(a));        memset(b,0,sizeof(b));        memset(result,0,sizeof(result));        int i,j,signi=0;//signi标记小数点的位置        size_t pos=r.find(".");//寻找小数点的位置        if(pos!=string::npos)//判断是否是整数            signi=(5-pos)*n;//记录有多少位小数        for(i=5,j=0; i>=0; i--)        {            if(r[i]!='.')            {                result[j]=a[j]=b[j]=r[i]-'0';                j++;            }        }        while(n>=2)//注意n=1的情况        {            n--;            memset(result,0,sizeof(result));            for(i=0; i<5; i++)            {                for(j=0; j<len; j++)                {                    if(!b[i])                        break;                    result[i+j]+=a[j]*b[i];                    result[i+j+1]+=result[i+j]/10;//处理进位                    result[i+j]=result[i+j]%10;                }            }            memcpy(a,result,sizeof(result));//转存结果,以便下次计算//            for(i=0; i<len; i++)//                a[i]=result[i];        }        int fronti=-1;        for(i=len-1; i>=signi; i--)//去除前面多余的0        {            if(result[i])            {                fronti=i;                break;            }        }        int lasti=-1;        for(i=0; i<signi; i++)//去除后面多余的0        {            if(result[i])            {                lasti=i;                break;            }        }        if(fronti!=-1)        {            for(i=fronti; i>=signi; i--)                cout<<result[i];        }        if(lasti!=-1)        {            cout<<".";            for(i=signi-1; i>=lasti; i--)                cout<<result[i];        }        cout<<endl;    }    return 0;}

1 0