solution for POJ 1001
http://poj.org/problem?id=1001
POJ的前几道题理解起来很简单,算法也不复杂,只是需要花很长的时间去调试。1001本质上就是一个大数运算的问题,大数乘法的问题可以采用分治法,具体参考这篇文章:http://blog.csdn.net/tjsinor2008/article/details/5625849。代码如下:
1 #include<stdio.h> 2 #include<memory.h> 3 4 unsigned char a[200],b[200],c[200]; 5 int lenA,lenB,maxlen,digit,p; /*a[200],b[200],c[200]用来存储数据,底数去掉小数点以后存储在a里面,从高位到低位每一位储存一个数字,b保存每次乘法的结果,将该结果加上小数点并去掉0前缀和后缀存在c里面,最后打印出来。要注意数组的长度*/ 7 int strlength(char* s) 8 { 9 int result=0; 10 while(*s!=0) 11 { 12 result++; 13 s++; 14 } 15 return result; 16 } 17
/*乘法函数的关键在于对c[i+j+1]和c[i+j]修改赋值的这个循环,仔细想想不难理解。需要注意的是c[i+j]+=(c[i+j+1]-'0')/10这一句可能会导致在循环结束的时候有数组的元素大于‘9’,所以最后需要再重新对计算结果从后往前遍历一遍,将该进位的进位。*/ 18 void mul(unsigned char* a,unsigned char* b) 19 { 20 int i,j; 21 lenA=strlength(a); 22 lenB=strlength(b); 23 maxlen=lenA+lenB; 24 25 for(i=0;i<maxlen;i++) 26 { 27 c[i]='0'; 28 } 29 c[maxlen]='\0'; 30 31 for(i=0;i<lenA;i++) 32 { 33 for(j=0;j<lenB;j++) 34 { 35 c[i+j+1]+=(a[i]-'0')*(b[j]-'0'); 36 c[i+j]+=(c[i+j+1]-'0')/10; 37 c[i+j+1]=(c[i+j+1]-'0')%10+'0'; 38 } 39 } 40 41 for(i=maxlen-1;i>=0;i--) 42 { 43 if(c[i]>'9') 44 { 45 c[i-1]+=(c[i]-'0')/10; 46 c[i]=(c[i]-'0')%10+'0'; 47 } 48 } 49 50 memcpy(b,c,maxlen+1); 51 } 52
/*把计算结果加上小数点并去除掉前置和后置的不必要的0。为避免不必要的麻烦,我先把小数点加到该加的位置上,然后再处理多余的0.需要注意的是如果最后结果是以‘.’也就是以小数/点结尾(比如底数本来就是整数),那么这个小数点也是不需要的。*/ 53 void trim() 54 { 55 int i,len,headzerocount,trailzerocount; 56 headzerocount=trailzerocount=0; 57 len=strlength(b); 58 memmove(b+len-digit+1,b+len-digit,digit+1); 59 b[len-digit]='.'; 60 61 for(i=0;i<len+1;i++) 62 { 63 if(b[i]=='0') 64 { 65 headzerocount++; 66 continue; 67 } 68 break; 69 } 70 for(i=len;i>=0;i--) 71 { 72 if(b[i]=='0') 73 { 74 trailzerocount++; 75 continue; 76 } 77 break; 78 } 79 for(i=0;i<len-headzerocount-trailzerocount+1;i++) 80 { 81 c[i]=b[i+headzerocount]; 82 } 83 if(c[len-headzerocount-trailzerocount]=='.') 84 { 85 c[len-headzerocount-trailzerocount]=0; 86 } 87 else 88 { 89 c[len-headzerocount-trailzerocount+1]=0; 90 } 91 } 92 93 int main() 94 { 95 int i; 96 97 //mul("98999","98999"); 98 99 while(scanf("%s %d",a,&p)!=EOF) 100 { 101 b[0]='1'; 102 b[1]=0; 103 for(i=0;i<5;i++) 104 { 105 if(a[i]=='.') 106 { 107 memmove(a+i,a+i+1,6-i); 108 digit=(6-i-1)*p; 109 break; 110 } 111 } 112 113 for(i=0;i<p;i++) 114 { 115 mul(a,b); //每次乘的结果都放在b里面 116 } 117 118 trim(); 119 printf("%s\n",c); //去掉0,加上小数点以后的值放在c里面 120 } 121 return 0; 122 }
总结一下,除了实现大数乘法和把小数点放到正确的位置上,还需要注意的细节有如下几点:
1.因为运算结果很有可能是比较长的字符串,所以需要注意不要让储存数据的数据结构有地址重叠的部分。
2.大数相乘时记得最后从后向前遍历一次把未进位的数位进位掉。
3.如果最终结果是个整数,那么小数点是没有用的,记得把它去掉。