题目描述
用高精度计算出 S=1!+2!+3!+⋯+n!(n≤50)。
其中 !
表示阶乘,定义为 n!=n×(n−1)×(n−2)×⋯×1。例如,5!=5×4×3×2×1=120。
输入格式
一个正整数 n。
输出格式
一个正整数 S,表示计算结果。
输入输出样例
输入 #1
3
输出 #1
9
分析
一看到题目,很多人会觉得这是一道很简单的题目,只需要利用递归的方法写一个阶乘的函数就可以了,就像下面这样:
#include<iostream>
#include<string.h>
#include<cmath>
#include<iomanip>
#include<algorithm>
using namespace std;
int factorial(int t) {
if (t == 1) {
return 1;
}
else {
return t * factorial(t - 1);
}
}
int main() {
int s = 0;
int n;
int i;
cin >> n;
for (i = 1; i <= n; i++) {
s += factorial(i);
}
cout << s << endl;
return 0;
}
然而,我们注意到,题目中n的数值最大可以达到50,而当n达到50时,我们可以通过计算器算出此时n!的值为3.04140932 * 10^64。这时,我们发现,n!的值已经远远超出了long long int能够表示的范围,因此,上面的这段代码在n稍微大一点的时候就会不适用。
既然无法用一个int型来表示所求的值,那么,我们为何不构建一个数组,将所求值的每一位都拆开来,分别用数组中的一个元素来表示呢?
由此,我们很自然地想到了利用高精度数组来代替普通的int型进行计算。关于高精度数组的相关运算,我在CSDN上面查了一下,都有很清楚的教学内容,所以在这里就只大概提一下,不做详述,也不通过代码举例。
首先,我们定义一个数组a[N]来表示一个大数,其中,a[0]表示这个大数的个位,a[1]表示这个大数的十位,······以此类推。注意,由于无法确定大数的位数,为了便于接下来的运算,在表示大数时,数组的顺序与大数的正常顺序是相反的,需要读者格外注意。
其次,由于 s=1!+2!+······+n!,因此,高精度数组的加法是必须的。对于高精度数组的加法,我们可以模拟人工竖式的计算:从低位开始,将两个数组的对应位置相加即可。高精度数组的加法中,最重要的一点就是进位:当某一位的和数大于等于10时,我们需要让下一位+1,而本位仅保留和数的个位部分。
再次,想要求出某个数的阶乘,我们还需要利用到高精度数组的乘法。要计算高精度数组的乘法,我们需要利用到乘法分配律的思想:将大数分解成a[0]+a[1]*10+a[2]*100+······+a[N]*10^N,然后分别与乘数相乘,最后再相加即可。
有了高精度数组的加法和乘法,要想求高精度数组的阶乘之和,只需要综合利用上述二者即可。于是,我们就可以写出代码了:
#include<iostream>
#include<string.h>
#include<cmath>
#include<iomanip>
#include<algorithm>
using namespace std;
int s[100]{};//表示s的高精度数组
int n;
int a[51][100]{};//分别用来表示1~n的阶乘的高精度数组
int i, j;
int flag = 0;
void add(int a[100], int b[100]) {//计算加法的函数,将b加到a上
int i;
for (i = 0; i < 100; i++) {
a[i] += b[i];//对每一位分别进行个位数加法
if (a[i] >= 10) {//如果得到的值不是个位数,则需要进位
a[i + 1] += 1;
a[i] -= 10;
}
}
}
void multiply(int a[100], int k) {//计算乘法的函数,其中,a[100]为大数,k为一个很小的数,能够用int来表示
int i, j;
int m;
int temp[100]{};//定义一个temp数组来暂时储存相乘后的结果
int t;
for (i = 0; i < 100; i++) {//对大数a,从第一位开始分别与k相乘
m = a[i] * k;
for (j = 0; m > 0; j++) {
temp[i + j] += m % 10;
if (temp[i + j] >= 10) {
temp[i + j] -= 10;
temp[i + j + 1] += 1;
}
m = floor(m / 10);
}//上面的for循环用于进位
}
for (i = 0; i < 100; i++) {
a[i] = temp[i];
}//最后将temp赋值给a
}
void factorial(int a[100], int t) {//计算阶乘t!的函数,其中a的初始值为1,即仅a[0] = 1,其他均为0
int i;
for (i = 1; i <= t; i++) {
multiply(a, i);
}//类比int型的阶乘函数
}
int main() {
cin >> n;
for (i = 1; i <= n; i++) {
a[i][0] = 1;
factorial(a[i], i);
add(s, a[i]);
}//计算阶乘之和
for (i = 99; i >= 0; i--) {
if (s[i] != 0) {
flag = 1;
}
if (flag == 1) {
cout << s[i];
}
}//在输出时,为了避免输出额外的0,我们可以定义一个flag,当出现第一个非0数时,开始输出
return 0;
}