题目
P1009 [NOIP1998 普及组] 阶乘之和
题目链接
https://www.luogu.com.cn/problem/P1009
知识点
求阶乘正常做法:
#include <stdio.h>
long long jiecheng(long long n)
{
long long a=1;
for(int i=1;i<=n;i++){
a*=i;
}
return a;
}
int main ()
{
int n;
long long sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
sum+=jiecheng(i);
}
printf("%lld",sum);
return 0;
}
本来应该是这样做就可以了,由于n的最大值是50,所以直接这么做会超出long long类型的范围,所以这道题需要用高精度的做法来做。
这里先补充一些C++的知识:结构体中的构造函数(类似于java中的构造方法)
结构体中的构造函数就是为了方便初始化变量用的,因为在结构体中变量无法直接初始化,需要写一个方法来对变量进行初始化,如果我们写的是一个普通方法,每当我们需要初始化变量的时候,就需要去调用这个方法一次,这样较为麻烦。所以我们就可以写一个构造函数,这个构造函数不需要调用(用户也无法调用),它会在创建对象(也就是结构体变量)时,自动调用。
例:
#include <stdio.h>
#include<string>
using namespace std;
struct student{
string name;
int age;
student() {//通过构造函数来进行初始化
name="jinx";
age=10;
}
};
int main ()
{
student stu;
printf("%s\n",stu.name.c_str());//printf无法直接输出string类型,需要c_str()函数转换一下
printf("%d",stu.age);
return 0;
}
输出就是jinx,10。
再学一下高精度的算法
在高精度算法中,大数是用一个一维数组来进行存储的,并且数组的低位用来存放数的低位,譬如一个大数为123456,此时a[0]存储的是6,a[1]存储的5,a[2]存储的是4 ... a[5]=1。
高精度的结构:
struct bignum{
int d[1001];
int len;
bignum{
memset(d,0,sizeof(d));
len=0;
}
}
高精度的存储:
bignum saven(char ds[]){//这个ds字符数组中存放的就是一个大数
bignum n;
n.len=strlen(ds);
for(int i=0;i<n.len;i++)
{
n.d[i]=ds[n.len-1-i]-'0';
}
return n;
}
高精度的比较:
//a>b 返回1,a<b 返回-1,a=b 返回0
int compare(bignum a,bignum b)
{
if(a.len>b.len) return 1;
else if(a.len<b.len) return -1;
else
{
for(int i=a.len-1;i>=0;i--)
{//从最高位往最低位开始比较
if(a.d[i]>b.d[i]) return 1;
else if(a.d[i]<b.d[i]) return -1;
}
}
return 0;
}
高精度加法:
例如:123+678=801
这个加法的思想就是:
1.先算8+3,8+3=11,取个位1为结果,并产生高位的进位1;
2.再算2+7,加上来自的进位的1,此时取个位的0为结果,再产生一个进位1;
3.最后计算1+6,再加上来自进位的1,此时没有进位,结果就为8。
最终就是801。
code:
bignum add(bignum a,bignum b)
{
bignum c;
int jinwei=0;
for(int i=0;i<a.len || i<b.len;i++)
{
int temp=a.d[i]+b.d[i];
c.d[c.len++]=temp%10;//这里的c的len初始为0,a和b的len都已经计算过
jinwei=temp/10;
}
if(jinwei!=0)
{
c.d[c.len++]=jinwei;
}
return c;
}
高精度减法:
例:321-189=132
1-9,不够减,向高位借1,高位-1,原数+10,变成11,11-9=2
2-8,不够减,要先减去被低位借的1,再向高位借1,高位-1,原数+10,2-1+10-8=3
3-1,先减去被低位借的1位,3-1-1=1
bignum sub(bignum a,bignum b){
bignum c;
for(int i=0;i<a.len || b.len;i++)
{
if(a.d[i]<b.d[i])
{
a.d[i+1]--;
a.d[i]+=10;
}
c.d[len++]=a.d[i]-b.d[i];
while(c.len>1 && c.d[len-1]==0)
{
c.len--;
}
}
return c;
}
高精度乘法(高精度×低精度):
假设a是高精度的数
bignum mul(bignum a,int b)
{
bignum c;
int jinwei=0;
for(int i=0;i<a.len;i++)
{
int temp=a.d[i]*b;
c.d[c.len++]=temp%10+jinwei;
jinwei=temp/10;
}
while(jinwei!=0)
{
c.d[c.len++]=jinwei;
jinwei/=10;
}
return c;
}
高精度除法(高精度÷低精度):
这里要注意的是除法是从被除数的最高位开始除的。
bignum div(bignum a,int b)
{
bignum c;
int yushu=0;
c.len=a.len;
for(int i=a.len-1;i>=0;i--)
{
int temp=a.d[i]*10+yushu;
c.d[i]=temp/b;
yushu=temp%b;
}
while(c.len>0 && c.d[len--]==0)
{
c.len--;
}
c.d[len--]=yushu;//这一步不是很明白
return c;
}
code
#include <stdio.h>
#include<cstring>
//#include<bits/stdc++.h>
struct bignum{
int d[1000];
int len;
bignum() {
len=0;
memset(d,0,sizeof(d));
}
};
bignum add(bignum a,bignum b)
{
bignum c;
int jinwei=0;
for(int i=0;i<a.len || i<b.len;i++)
{
int temp=a.d[i]+b.d[i]+jinwei;
c.d[c.len++]=temp%10;
jinwei=temp/10;
}
if(jinwei!=0)
{
c.d[c.len++]=jinwei;
}
return c;
}
bignum mul(bignum a,int b)
{
bignum c;
int jinwei=0;
for(int i=0;i<a.len;i++)
{
int temp=a.d[i]*b+jinwei;
c.d[c.len++]=temp%10;
jinwei=temp/10;
}
while(jinwei!=0)
{
c.d[c.len++]=jinwei%10;
jinwei/=10;
}
return c;
}
//void print(bignum a)
//{
// for(int i=a.len-1;i>=0;i--)
// {
// printf("%d",a.d[i]);
// }
//}
int main ()
{
int n;
scanf("%d",&n);
bignum a,sum;
a.d[0]=1;
a.len=1;
for(int i=1;i<=n;i++)
{
a=mul(a,i);//这里就相当于上面的a=a*i;只是由于a太大,所以得用高精度表示
sum=add(sum,a);
}
for(int i=sum.len-1;i>=0;i--)
{
printf("%d",sum.d[i]);
}
//print(sum);
return 0;
}
小结
把以前没怎么学好的高精度捡起来再学了一下,发现网上的教程基本上都能看懂了,感觉学一下算法还是蛮有意思的吧,挺能锻炼自己的思维。这道题目的话只用到了高精度×低精度的情况,可能后面还会遇到高精度×高精度的情况,或者是高精度除法,以后遇到的时候,再继续学一下吧。
参考文章
https://blog.csdn.net/qq_46702358/article/details/120225315?spm=1001.2014.3001.5501
https://blog.csdn.net/qq_46702358/article/details/120276567
标签:bignum,P1009,int,高精度,len,--,long,阶乘,洛谷 From: https://www.cnblogs.com/Jinx8823/p/16890169.html