阶乘之和 (洛谷 P1009题)

首先,题目传送门是一定要有的
接下来就是题目描述了

题目描述

用高精度计算出S=1!+2!+3!+…+n! (n≤50) 其中“!”表示阶乘,例如:5!=5×4×3×2×1

输入格式

一个正整数N

输出格式

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

输入输出样例

输入 #1

3

输出 #1

9

说明/提示

注:《深入浅出基础篇》中使用本题作为例题,但是其数据范围只有 n<=20,使用书中的代码无法通过本题。
如果希望通过本题,请继续学习第八章高精度的知识。

好了,题目看完了,首先肯定要定义变量对吧,因为考虑到1!+2!+3!+…+50!可能会很大,所以定义的变量要尽可能地大,因为题目限制内存在125MB之内,所以int类型的数组最大的长度为125MB/4B=32768000考虑到后面还会有变量,故把数组长度设置为3276800接下来就是高精度阶乘和高精度加法,如果你学习过高精度,接下来的部分你可以掠过,没有学习过的可以看一下加粗部分的字

首先是高精度加法,其实就是循环把每一位都加起来,比如42954+69837,计算过程如下:
4+7=11,11>=10,所以进位就是1,这一位就变成了1;
3+5+1=9,9<10,所以进位就是0,这一位就还是9;
8+9=17,17>=10,所以进位就是1,这一位就变成了7;
2+9+1=12,12>=10,所以进位就是1,这一位就变成了2;
4+6+1=11,11>=10,所以进位就是1,这一位就变成了1;
所以最后的结果为112791
乘法也一样,只不过多了些进位而已,知道了这些,就可以做高精度阶乘了.

接下来,上代码!

源代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <climits>
using namespace std;
int fac[16328],sum[16328]={0};//fac代表每次的阶乘数,sum代表最后加和
int nmax=INT_MIN;//随后输出数量取fac的最大长度,所以需要nmax
int factorial(int n){//定义求阶乘函数
  	fac[1]=1;//首先将fac设为1
  	int mul=1,len=1;//mul为每次的乘数,len为fac的长度
  	while(n--){
	  	int carry=0;//carry为进位
	  	for(int i=1;i<=len;i++){
	  		fac[i]=fac[i]*mul+carry;//下一个数等于上一个数乘以乘数加进位
	  		carry=fac[i]/10;//进位等于求出的数的除个位以外的数
	  		fac[i]=fac[i]%10;//而留下的数则是各位
		}
		mul++;//每次乘数变大1
		while(carry){
		  	fac[++len]=carry%10;//在len长度以外的数用这种方法处理
		  	carry/=10;//随后只留下carry的个位
		}
  	}
 	return len;//以后要用,需要返回
}
int main(){
  	int n,tmptmp=0;
  	cin>>n;//输入n;
  	for(int i=1;i<=n;i++){
	  	int tmp=factorial(i);//得到len并赋给tmp
	  	if(tmp>nmax)	nmax=tmp;//打擂台求最大
		for(int j=1;j<=tmp;j++){
	  		sum[j]+=fac[j];
	  		sum[j]+=tmptmp;//tmptmp为进位 
	  		if(sum[j]>=10){
	  			tmptmp=1;//如果>=10则进一位
	  			sum[j]-=10;//然后求出这一位的真正数值
			}else	tmptmp=0;//否则进位归零
		}
	} 
	for(int i=nmax;i>=1;i--)	cout<<sum[i];//反向输出
  return 0;
}
<think>嗯,用户这次问的是洛谷P1009用C语言怎么写,还要求给出代码。首先,我得先确认这道的具体内容。洛谷目编号P1009是“阶乘之和”,对吧?目要求计算1! + 2! + 3! + ... + n!的值,其中n的范围是1到50。因为阶乘增长得非常快,n=50的时候结果会非常大,普通的整数类型肯定会溢出,所以必须用高精度计算来处理。 用户需要的是C语言的代码,所以我要考虑如何用C实现高精度运算。C语言没有内置的大整数支持,所以需要自己用数组来模拟。首先,我需要确定如何表示大数。通常的做法是用一个数组,每个元素存储一位数字,或者更高效地,每个元素存储多位数字以减少计算次数。不过对于这道,可能用每位存储一个数字会更简单,尤其是考虑到用户可能对高精度运算不太熟悉。 接下来,我需要分别实现阶乘的计算和累加的过程。每次计算i的阶乘时,可以复用前一次的结果乘以i,这样可以减少重复计算。例如,5! = 4! * 5。同样,每次阶乘的结果需要存储在高精度数组中,然后将其加到总和的高精度数组中。 然后,我需要考虑乘法和加法的实现。乘法的话,每一位乘以当前的i,然后处理进位。加法则是将阶乘的结果加到总和的数组中,同样需要处理进位。这里需要注意的是,乘法和加法的进位处理可能会不同,需要仔细处理每一步的进位。 另外,数组的大小需要足够大。比如50!的位数大约是65位左右,但累加的话总和的最大位数可能更多,不过具体需要多少位呢?可能需要计算一下。例如,50!是大约65位,总和是1!+2!+...+50!,最大的数还是50!,所以总和的位数不会超过65位,但为了安全起见,可以分配更大的数组,比如200位,确保不会溢出。 然后,代码的结构大致是:初始化总和数组为0,然后循环从1到n,每次计算当前i的阶乘,然后加到总和里。每次计算阶乘时,用上一个阶乘的结果乘以i,然后处理进位。每次加法时,将阶乘数组加到总和数组,处理进位。 需要定义两个高精度数组,一个保存当前的阶乘,一个保存总和。例如,current_fact存储i!,sum存储总和。初始时current_fact为1(1! = 1),sum初始为0。然后从1开始循环,每次将current_fact乘以i得到新的current_fact(即i!),然后将current_fact加到sum里。 需要注意的是,数组的存储顺序,通常是从低位到高位还是高位到低位。这里可能需要从低位到高位存储,这样进位处理更方便。例如,数组的第0位是个位,第1位是十位,依此类推。这样在计算的时候,进位可以向前推进。 举个例子,假设计算3!:current_fact初始是1(即[1,0,0,...]),然后乘以2得到2(处理进位),再乘以3得到6。然后sum加上1! =1,sum=1;加上2! =2,sum=3;加上3! =6,sum=9,这样结果正确。 然后,如何输出结果呢?需要从最高位开始遍历数组,找到第一个非零的位置,然后倒序输出,因为数组是从低位到高位存储的。例如,数组存储的是个位、十位、百位,所以最高位在数组的末尾,需要从后往前找到第一个非零的位置,然后从那里开始逆序输出。 现在,我需要考虑代码的具体实现。首先定义数组的大小,比如MAX_DIGITS 200。然后两个数组current_fact和sum,初始化为0,current_fact[0] = 1(因为1! =1)。 然后循环从1到n,每次计算current_fact乘以i。乘法的过程是遍历current_fact的每一位,乘以i,然后处理进位。例如: for (int i = 1; i <= n; i++) { // 计算current_fact *= i int carry = 0; for (int j = 0; j < MAX_DIGITS; j++) { int temp = current_fact[j] * i + carry; current_fact[j] = temp % 10; carry = temp / 10; } // 这里可能carry不为0,但数组足够大,可以忽略?或者需要处理? // 需要确保数组足够大,所以carry应该被处理完,否则会溢出,但目中n=50,所以200位应该足够。 // 然后将current_fact加到sum中 carry = 0; for (int j = 0; j < MAX_DIGITS; j++) { int temp = sum[j] + current_fact[j] + carry; sum[j] = temp % 10; carry = temp / 10; } } 然后输出的时候,找到sum的最高位非零的位置,从那里开始逆序输出每一位。 需要注意的是,当n=0的时候可能需要处理,但目中n>=1,所以不需要考虑。另外,初始化current_fact的时候,可能应该从1开始,比如i=1的时候,current_fact是1,然后循环从2到n,乘以i?或者循环从1到n? 比如,初始current_fact是1,对应1!,然后循环从2到n,每次乘以i得到i!。或者循环从1到n,每次乘以i得到i!。可能需要调整循环的起始值。 或者,初始current_fact为1,sum初始为0。然后循环i从1到n: 第一次循环i=1,current_fact *=1 → 1! =1,sum +=1 → sum=1。 第二次i=2,current_fact *=2 → 2! =2,sum +=2 → sum=3. 第三次i=3,current_fact *=3 →6,sum +=6 → sum=9. 这样正确。所以循环应该是从1到n,每次乘以i得到i!,然后加到sum里。所以初始current_fact是1,对应i=1时的阶乘,然后在循环中,i从1到n,每次乘以i得到i!,然后加到sum里。不过这样i=1的时候乘以1,得到1!,然后加到sum里。然后i=2时乘以2,得到2!,然后加到sum里,以此类推。这样是正确的。 所以代码的结构应该是: 初始化current_fact为1,sum为0。 for (i = 1; i <=n; i++) { current_fact = current_fact * i; // 这里通过高精度乘法实现 将current_fact加到sum中; } 然后输出sum的值。 现在,代码的具体实现。比如,数组的初始化,循环中的乘法处理,加法处理,以及最后的输出。 需要定义两个数组,初始时current_fact数组为0,只有current_fact[0] =1。sum数组全为0。 然后循环i从1到n: 乘法部分: int carry = 0; for (int j=0; j<MAX_DIGITS; j++) { int temp = current_fact[j] * i + carry; current_fact[j] = temp %10; carry = temp /10; } // 这里如果carry不为0,可能需要处理,但假设数组足够大,不会有溢出。因为50!的位数在65位左右,所以200足够。 加法部分: carry =0; for (int j=0; j<MAX_DIGITS; j++) { int temp = sum[j] + current_fact[j] + carry; sum[j] = temp%10; carry = temp/10; } 这样循环之后,sum就是总和。 最后输出sum数组,从最高位开始找到第一个非零的数字,然后输出后面的所有数字。 例如,从MAX_DIGITS-1开始倒序遍历,找到第一个非零的位置,然后从该位置到0输出每一位。不过因为数组存储的是个位在0,十位在1,所以最高位在数组的后面。例如,数字1234存储为[4,3,2,1,0,0,...],所以输出的时候应该从最高非零位开始,逆序输出每一位。例如,最高位是3的位置(值为1),然后输出1,2,3,4。 或者,代码中需要从后往前找到第一个非零位,然后从该位开始向前输出每一位的数字。例如: int start = MAX_DIGITS -1; while (start >=0 && sum[start] ==0) start--; if (start ==-1) printf("0"); // 但sum至少是1,所以不会出现 else { for (int i=start; i>=0; i--) { printf("%d", sum[i]); } } 这样就能正确输出结果。 现在,把这些思路转化为C代码。需要处理数组的初始化,循环,乘法和加法的进位,以及输出。 可能的代码结构: #include <stdio.h> #define MAX_DIGITS 200 int main() { int n; scanf("%d", &n); int sum[MAX_DIGITS] = {0}; int current_fact[MAX_DIGITS] = {0}; current_fact[0] = 1; // 初始为1! =1 for (int i = 1; i <= n; i++) { // 计算current_fact *= i int carry = 0; for (int j = 0; j < MAX_DIGITS; j++) { int temp = current_fact[j] * i + carry; current_fact[j] = temp % 10; carry = temp / 10; } // 将current_fact加到sum中 carry = 0; for (int j = 0; j < MAX_DIGITS; j++) { int temp = sum[j] + current_fact[j] + carry; sum[j] = temp % 10; carry = temp / 10; } } // 输出结果 int start = MAX_DIGITS - 1; while (start >= 0 && sum[start] == 0) { start--; } if (start == -1) { printf("0"); } else { for (int i = start; i >= 0; i--) { printf("%d", sum[i]); } } return 0; } 这样应该可以解决问。不过需要测试一下,例如n=3的时候是否输出9,n=10的时候是否正确。 不过可能存在一个问,当i=1时,current_fact是1,然后加到sum里,得到sum=1。然后i=2时,current_fact乘以2得到2,加到sum得到3。i=3时,current_fact乘以3得到6,sum=9。正确。 另一个测试点,比如n=1,应该输出1。n=2,输出3。n=3,9,正确。 对于更大的n,比如n=10,总和是4037913。这个需要验证是否正确。 但可能需要注意,在乘法过程中,如果carry在数组末尾没有被处理,比如当数组长度不够时,会导致错误。但目中n最大是50,所以MAX_DIGITS设为200足够。 所以这样的代码应该可以解决问。最后,用户可能需要完整的代码,所以需要将上述代码整理好,并添加注释说明。</think>洛谷P1009目要求计算阶乘之和:$S = 1! + 2! + 3! + \dots + n!$($n \leq 50$)。由于结果极大,需用**高精度算法**处理。以下是C语言实现代码: ```c #include <stdio.h> #define MAX_DIGITS 200 // 预设足够大的位数 int main() { int n; scanf("%d", &n); int sum[MAX_DIGITS] = {0}; // 存储总和 int current_fact[MAX_DIGITS] = {0}; // 存储当前阶乘值 current_fact[0] = 1; // 初始化为1! = 1 for (int i = 1; i <= n; i++) { // 计算 current_fact = current_fact * i int carry = 0; for (int j = 0; j < MAX_DIGITS; j++) { int temp = current_fact[j] * i + carry; current_fact[j] = temp % 10; carry = temp / 10; } // 将当前阶乘值累加到总和 carry = 0; for (int j = 0; j < MAX_DIGITS; j++) { int temp = sum[j] + current_fact[j] + carry; sum[j] = temp % 10; carry = temp / 10; } } // 输出结果(去除前导零) int start = MAX_DIGITS - 1; while (start >= 0 && sum[start] == 0) start--; for (int i = start; i >= 0; i--) { printf("%d", sum[i]); } return 0; } ``` #### 代码解析 1. **高精度存储** - 使用数组`sum`和`current_fact`按**低位到高位**存储数字(例如数字`123`存为`[3,2,1,0,...,0]`) 2. **阶乘计算** - 动态计算$i! = (i-1)! \times i$,通过循环处理每一位的乘法和进位 3. **累加操作** - 将每个阶乘值按位加到总和数组,处理进位 4. **结果输出** - 从最高非零位开始逆序输出,得到正确结果 #### 测试示例 输入: ``` 3 ``` 输出: ``` 9 ``` 对应计算:$1! + 2! + 3! = 1 + 2 + 6 = 9$
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值