【题目链接】
ybt 1173:阶乘和
注:一本通上这题,应该把
n
≤
50
n\le50
n≤50当做
n
≤
100
n\le100
n≤100来看
OpenJudge NOI 1.6 15:阶乘和
洛谷 P1009 [NOIP1998 普及组] 阶乘之和
【题目考点】
1. 高精度
考察:高精乘低精 高精加高精
高精度计算讲解
【解题思路】
相应的低精度数字求阶乘和的解法如下:
ybt 1091:求阶乘的和
采用其中复杂度为
O
(
n
)
O(n)
O(n)的迭代法,代码如下:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
int s = 0, a = 1;//a:某一阶乘项的值
for(int i = 1; i <= n; ++i)
{
a *= i;//本句运行结束后,a的值为i!
s += a;//加和变量中增加i!
}
cout << s;
return 0;
}
在本问题中,s与a应该是高精度数字,i,n是低精度数字。a*=i
为高精乘低精,s+=a
为高精加高精。
该题难点在于分析结果位数,为数字数组设一个合理的数组长度。(比如不知道结果位数,就设了数组长为10000,那你凭什么认为结果一定小于10000位?总得有点根据吧。)
求数字位数公式:假设数字x有n位,那么
n
=
⌊
l
g
x
⌋
+
1
n = \lfloor lgx \rfloor + 1
n=⌊lgx⌋+1
假设n=50,求
1
!
+
2
!
+
.
.
.
+
50
!
1!+2!+...+50!
1!+2!+...+50!的位数,我们可以不断将结果放大
1
!
+
2
!
+
.
.
.
+
50
!
≤
50
∗
50
!
≤
50
∗
5
0
50
=
5
0
51
1!+2!+...+50! \le 50*50! \le 50*50^{50}=50^{51}
1!+2!+...+50!≤50∗50!≤50∗5050=5051
求
5
0
51
50^{51}
5051的位数:
⌊
l
g
5
0
51
⌋
+
1
=
⌊
51
∗
l
g
50
⌋
+
1
≤
⌊
51
∗
l
g
100
⌋
+
1
=
103
\lfloor lg50^{51} \rfloor + 1 = \lfloor 51*lg50 \rfloor + 1 \le \lfloor 51*lg100 \rfloor + 1 = 103
⌊lg5051⌋+1=⌊51∗lg50⌋+1≤⌊51∗lg100⌋+1=103
将数字数组长度设为105就可以满足要求。
【注】一本通OJ上本题测试数据有误,需要将数字数组长度设为155才能过(我只试到155能过),否则有几个测试点会报“运行错误”。所以可以当这道题中给定的
n
≤
50
n\le50
n≤50当做
n
≤
100
n\le100
n≤100来看,这样推算出的数据位数就没有问题了。
【题解代码】
解法1:使用函数与数组
本解法中,使用的高精度运算为 a*=b和a+=b。
#include<bits/stdc++.h>
using namespace std;
#define N 105
void setLen(int a[], int i)
{
while(a[i] == 0 && i > 1)
i--;
a[0] = i;
}
void Multiply(int a[], int b)//高精乘低精 a *= b
{
int c = 0, i;
for(i = 1; i <= a[0]; ++i)
{
a[i] = a[i]*b + c;
c = a[i] / 10;
a[i] %= 10;
}
while(c > 0)
{
a[i] = c % 10;
c /= 10;
i++;
}
setLen(a, i);
}
void Add(int a[], int b[])//高精加高精 a += b
{
int c = 0, i;
for(i = 1; i <= a[0] || i <= b[0]; ++i)
{
a[i] += b[i] + c;
c = a[i] / 10;
a[i] %= 10;
}
if(c > 0)
a[i] = c;
setLen(a, i);
}
void showNum(int a[])
{
for(int i = a[0]; i >= 1; --i)
cout << a[i];
}
int main()
{
int a[N] = {1, 1}, s[N] = {1, 0}, n;//数字a初值为1 加和s初值为0
cin >> n;
for(int i = 1; i <= n; ++i)
{
Multiply(a, i);
Add(s, a);
}
showNum(s);
return 0;
}
解法2:类中重载运算符
#include<bits/stdc++.h>
using namespace std;
#define N 155
struct HPN
{
int a[N];
HPN()
{
memset(a, 0, sizeof(a));
}
HPN(char s[])
{
memset(a, 0, sizeof(a));
a[0] = strlen(s);
for(int i = 1; i <= a[0]; ++i)
a[i] = s[a[0] - i] - '0';
}
int& operator [] (int i)
{
return a[i];
}
void setLen(int i)//确定数字位数
{
while(a[i] == 0 && i > 1)
i--;
a[0] = i;
}
void operator *= (int b)//a *= b
{
int c = 0, i;
for(i = 1; i <= a[0]; ++i)
{
a[i] = a[i]*b + c;
c = a[i] / 10;
a[i] %= 10;
}
while(c > 0)
{
a[i] = c % 10;
c /= 10;
i++;
}
setLen(i);
}
void operator += (HPN b)//高精加高精
{
int c = 0, i;
for(i = 1; i <= a[0] || i <= b[0]; ++i)
{
a[i] += b[i] + c;
c = a[i] / 10;
a[i] %= 10;
}
a[i] = c;
setLen(i);
}
void show()
{
for(int i = a[0]; i >= 1; --i)
cout << a[i];
}
};
int main()
{
int n;
cin >> n;
HPN s("0"), a("1");//s = 0, a = 1
for(int i = 1; i <= n; ++i)
{
a *= i;//高精乘低精
s += a;//高精加高精
}
s.show();
return 0;
}