算法题目讲解--阶乘之和

 今天讲解的题目来自洛谷平台 (P1009 [NOIP1998 普及组] 阶乘之和)

题目

 

 思路

通过题目可以大体看出这道题需要完成阶乘求和两个算法,通过循环从1遍历到n,先算阶乘再求和

根据n的范围,我们知道n最大可以取到50,那么仅仅计算 n!就已经可以远超 long long 类型的储存范围了,此时需要考虑使用高精度加法和乘法

如果不会这两种算法可以去看之前写过的两篇算法讲解

高精度算法--大数加法-CSDN博客

高精度乘法-CSDN博客 

 

 代码实现
 创建变量

首先创建两个数组用于保存每一步阶乘与求和的值

mul用于阶乘  add用于求和

为了方便操作,两个数组设置为全局变量;

int mul[110] = { 0 }, add[110] = { 0 };
主函数结构

 为了使代码清晰明了,将阶乘和求和的功能封装为函数,这一步先写主函数的结构

int main()
{
	mul[100] = 1;//将两个数组中下标为100的元素设置为1,表示1的阶乘和求和
	add[100] = 1;

	int n;//创建变量n用于保存输入
	cin >> n;

	for (int i = 2; i <= n; i++)//1的阶乘和求和已经在前面完成,所以循环从2开始,到n结束
	{
		Mul(i);//求阶乘的函数
		Add();//求和的函数
	}
	print();//打印结果的函数

	return 0;
}
阶乘函数
  1. 创建中间变量t,用于保存进位
  2. 数组中下标100处是数位的第一位,所以从100遍历到0
  3. 第一步,用当前数位的数乘以 x 再加上进位数
  4. 第二步,计算上一步结果的进位,用t保存进位数
  5. 第三步,模拟十进制,将当前数位的值更新为进位后的数

 注:因为每一次计算阶乘都是连续的,例如:2! = 1 × 2 ,3! = 1 × 2 × 3,n! = (n-1)! × n

        这个函数中 x 相当于上式的n,数组mul相当于(n-1)!,函数结束计算之后的数组mul相当于n!

         因此不需要对每个数都完整的循环求阶乘,可以很大程度减少时间

void Mul(int x)
{
	int t = 0;
	for (int i = 100; i >= 0; i--)
	{
		mul[i] = mul[i] * x + t;
		t = mul[i] / 10;
		mul[i] %= 10;
	}
}
 求和函数

(求和函数与阶乘函数的原理基本相同,都是按数位依次处理数值)

  1. 创建中间变量t,用于保存进位
  2. 数组中下标100处是数位的第一位,所以从100遍历到0
  3. 第一步,将上一个数阶乘的结果(数组mul)与数组add按数位相加,再加上进位数
  4. 第二步,计算当前数位的进位数,用t保存
  5. 第三步,模拟十进制,将当前数位的值更新为进位后的数

注:数组add保存的值是每次阶乘之后累加的结果

void Add()
{
	int t = 0;
	for (int i = 100; i >= 0; i--)
	{
		add[i] = add[i] + mul[i] + t;
		t = add[i] / 10;
		add[i] %= 10;
	}
}
 打印函数

因为数字最低位在最右端,所以打印时需要从左端开始遍历

  1. 创建变量s用于保存结果最高位的下标 
  2. 第一个for循环用来寻找结果的最高位,消除前导0,找到最高位时用s保存下标,跳出循环
  3. 第二个for循环用来遍历打印结果,从下标s处开始,到下标100处结束

注: 初始化后的数组add中未被修改的元素一定为0,所以寻找最高位只需要从下标0开始遍历,如果add[i]处元素不为0那当前位置就是最高位

void print()
{
	int s = 0;
	for (int i = 0; i <= 100; i++)
	{
		if (add[i] != 0)
		{
			s = i;
			break;
		}
	}
	for (int i = s; i <= 100; i++)
	{
		printf("%d",add[i]);
	}
}
注意

如果构造函数写在主函数之后,不要忘记在主函数之前写函数头,否则函数无法使用

阶乘和求和函数中进位与更新数值的步骤顺序不能写反

完整代码
#include <iostream>
using namespace std;

int mul[110] = { 0 }, add[110] = { 0 };

void Mul(int x)
{
	int t = 0;
	for (int i = 100; i >= 0; i--)
	{
		mul[i] = mul[i] * x + t;
		t = mul[i] / 10;
		mul[i] %= 10;
	}
}

void Add()
{
	int t = 0;
	for (int i = 100; i >= 0; i--)
	{
		add[i] = add[i] + mul[i] + t;
		t = add[i] / 10;
		add[i] %= 10;
	}
}

void print()
{
	int s = 0;
	for (int i = 0; i <= 100; i++)
	{
		if (add[i] != 0)
		{
			s = i;
			break;
		}
	}
	for (int i = s; i <= 100; i++)
	{
		cout << add[i];
	}
}

int main()
{
	mul[100] = 1;
	add[100] = 1;

	int n;
	cin >> n;

	for (int i = 2; i <= n; i++)
	{
		Mul(i);
		Add();
	}
	print();

	return 0;
}

 

如果文章内容有误,欢迎指正

  • 42
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值