洛谷刷题_P1009 [NOIP1998 普及组] 阶乘之和

题目

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]+jinwei;
        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

posted @ 2022-11-14 19:48  Jinx8823  阅读(76)  评论(0)    收藏  举报