洛谷[NOIP1998 普及组] 阶乘之和 (C语言,高精度加法,高精度×低精度,高精度阶乘)

[NOIP1998 普及组] 阶乘之和

题目描述

用高精度计算出 S = 1 ! + 2 ! + 3 ! + ⋯ + n ! S = 1! + 2! + 3! + \cdots + n! S=1!+2!+3!++n! n ≤ 50 n \le 50 n50)。

其中 ! 表示阶乘,定义为 n ! = n × ( n − 1 ) × ( n − 2 ) × ⋯ × 1 n!=n\times (n-1)\times (n-2)\times \cdots \times 1 n!=n×(n1)×(n2)××1。例如, 5 ! = 5 × 4 × 3 × 2 × 1 = 120 5! = 5 \times 4 \times 3 \times 2 \times 1=120 5!=5×4×3×2×1=120

输入格式

一个正整数 n n n

输出格式

一个正整数 S S S,表示计算结果。

样例 #1

样例输入 #1

3

样例输出 #1

9

提示

【数据范围】

对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 50 1 \le n \le 50 1n50

【其他说明】

注,《深入浅出基础篇》中使用本题作为例题,但是其数据范围只有 n ≤ 20 n \le 20 n20,使用书中的代码无法通过本题。

如果希望通过本题,请继续学习第八章高精度的知识。

NOIP1998 普及组 第二题

分析

  1. 我们分开计算,因为求得是阶乘之和,我们可以先算每个的阶乘(用到高精度阶乘),算好阶乘之后我们在相加(用到高精度加法);
  2. 高精度加法以及其他高精度算法可点击查看我往期文章,这里我着重讲一下高精度阶乘;
    ps:本来昨天写完高精度×高精度我还很开心,结果今天刷这题就打击到我了,居然还有个高精度×低精度,唉,学无止境啊,高精度系列算法看的太多,知识来源基本都来源于b站,指路:麦克老师讲算法、信息学万老师等。
  3. 其实原理与高精度×低精度很类似,区别在与:
    (1)高精度阶乘数组长度未知,动态生成。
    (2)高精度阶乘不需要翻转数组。
    (PS:这些都不重要,把核心思想弄明白,我讲的这些就自然而然明白了,我写的越多,你们可能越不想看)
  4. 此处补充一下高精度×低精度的思想:
    (1)将高精度的数拆开用数组存储,具体方法为定义 char 类型数组s存储要输入的值,再将其转化成 int 型存入 int 型数组a中,为了方便操作,再将其转置(以上步骤都与我最初写的高精度加法一样);
    (2)低精度看题目要求是用 int 还是 long long;
    (3)拆开存入数组a后,逐位与低精度相乘,再进位,此处进位与加法进位不同,最后可能有多位进位,需单独处理;
    (4)核心代码:
int jinWei = 0;
	for (int i = 0; i < lena; i++) {
		a[i] = a[i]*n + jinWei;
		jinWei = a[i] / 10;
		a[i] = a[i] % 10;
	}
	
	//处理进位
	while (jinWei) {
		a[lena++] = jinWei % 10;
		jinWei = jinWei / 10;
	}

(5)别忘了处理前导0和逆着输出;
(6)图示理解(千言万语尽在图中):
在这里插入图片描述
5. 阶乘,其实阶乘的核心代码也是上面的那个,只不过阶乘是 1×2×3×…×n,可以当成是高精度数 ×1×2×…×n,把高精度数初值设为1,即a[1] = 1,lena = 1,然后 ×1×2×…×n,×n个低精度的数而已,每次在乘的时候处理进位的同时,lena也会变(咋感觉自己罗里吧嗦的)
6. 阶乘完以后就可以调用高精度加法方法来求和了,最后输出结果完美结束!

完整代码

#include <stdio.h>

int c[10010] = {0};//数组c存放阶乘之和
int lenc = 0;

//高精度加
void add(int a[10010], int lena) {
	if (lena >= lenc) {
		lenc = lena + 1;
	}
	for (int i = 0; i < lenc; i++) {
		c[i] += a[i];
		c[i+1] += c[i] / 10;
		c[i] = c[i] % 10;
	}
}

void multi (int n) {	
	int a[10010] = {0};//数组a存放每一个阶段的阶乘
	int lena = 1;
	
	//高精度乘法(高精度×低精度)
	a[0] = 1;
	for (int i = 1; i <= n; i++) {//实现阶乘
		int jinWei = 0;
		for (int j = 0; j < lena; j++) {
			a[j] = a[j] * i + jinWei;
			jinWei = a[j] / 10;
			a[j] = a[j] % 10; 
		}
	
		//处理进位
		while (jinWei){
			a[lena++] = jinWei % 10;//数组a的长度动态增加,无法确定长度
			jinWei = jinWei / 10;
		}
	}

	add (a, lena);//调用高精度加法方法
}

int main () {
	int n, lena;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		multi(i);//调用阶乘方法
	}
	
	//处理前导0
	lenc = lenc - 1;
	while (c[lenc] == 0 && lenc >= 1){
		lenc--;
	}
	
	//输出
	for (int i = lenc; i>=0; i--) {
		printf("%d", c[i]);
	}
}

Last but not least !!!
我写了这么多高精度算法,最容易犯的错误就是,我经常写错是 / 还是 %,一找找半天
本位是取余,进位是除。

若果有错,欢迎大家斧正!ヾ(✿゚▽゚)ノ

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱写代码的小鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值