题目链接
N!这个题目在算法竞赛入门经典 出现过,过程相当于模拟手算的过程(我的大数乘法也是这样的)。假使我们的现在知道(n-1)! ,那么n!=(n-1)!*n.这个也是显而易见的。
但是n!的结果十分的大,10000!差不多需要45000位数字。所以我们不能简单的使用一个基础类型去进行存储,所以我们想到了另一个东西--------数组,对,没错就是数组,我们可以模拟我们手算的方式来进行计算。
假设我们要求解7!,已知6!,手算过程如下
6!=720.
7 2 0
* 7
--------------------------------
0
1 4
4 9
------------------------------------
5 0 4 0
这个过程相当简单,我们也就是拿7和6!的每一位相乘,然后按照位置加下来就可以了。当然写程序一般我们不会那么做,我们先将乘数与被乘数的第i位相乘得到的结果和上一次的进位结果加起来与10取模(既是求得当前位),放在存储结果的数组的第i位中,记录这次运算的进位(结果除以10就是进位结果),这样循环即可。
result[0]=1;//result是存储结果的数组 //我们存放的顺序是逆序的,以便于我们进位,例如6!=720,你们result存放的是 result[0]=0,result[1]=2,result[2]=7; for(int i=2;i<=n;i++) { int t=0;//进位结果,初始化进位为0 for(int j=0;j<MAXN;j++) { int temp=result[j]*i+t;//被乘数的第i位与乘数相乘加上进位的结果 result[j]=temp%10;//实际上存储的第i位的大小 t=temp/10000;//取得这次运算的进位结果 } } //输出的时候逆序输出即可,记得处理前导零
这样做是没有问题的。但是面对hdu1042需要求10000!这么搞?我一开始使用上面的方法做,果断的TLE(超时),看了别人的一些资料后发现,还可以对上面的代码进行改进。我们可以观察,我们的result数组只存一位,那如果我们的result存的不是一位而是几位是不是会使我们的计算次数变少?怎么做?其实也就是简简单单改一下代码中mod的数和除的数。
result[0]=1; for(int i=2;i<=n;i++) { int t=0; int k=0; for(int j=0;j<MAXN;j++) { int temp=result[j]*i+t; result[j]=temp%10000;//改了这儿 t=temp/10000;//改了这儿 } }
Q:上面提到的是4位一块,那能不能在提高欸例如6位7位?
A:可以的,分几块基于基础的数据类型,假设你使用int定义一个块,那么必须保证两个块的最大值相乘能够被一个int装起来,不会出现溢出。所以int可以装5位,如果6位和6位会超,可以选用64位整形存储。位数可达9位。
注意:注意多位存储的时候注意0的输出
贴下我的代码:
#include <iostream> #include <stdio.h> #include <cstring> using namespace std; const int MAXN=20000; int result[MAXN]; void fac(int n) { memset(result,0,sizeof(result)); result[0]=1; for(int i=2;i<=n;i++) { int t=0; int k=0; for(int j=0;j<MAXN;j++) { int temp=result[j]*i+t; result[j]=temp%10000; t=temp/10000; } } int i=MAXN-1; for(;!result[i];i--); cout<<result[i]; for(i-=1;i>=0;i--) { if(result[i]<10){ cout<<"000"; } else if(result[i]<100){ cout<<"00"; } else if(result[i]<1000){ cout<<"0"; } cout<<result[i]; } cout<<endl; } int main() { int n; while(cin>>n) { fac(n); } return 0; }
如果我的这篇文中出现什么问题,欢迎批评指正。