东华oj-进阶题第48题-求N!

在这里插入图片描述

48 求N!

作者: xxx时间限制: 1S章节: 一维数组

问题描述 :

给你一个整数N(0 ≤ N ≤ 10000),你的任务是计算并输出 N! 输入说明 :

输入多行,每行一个N。 输出说明 :

对于每个输入N,在一行中输出N!

行首与行尾为空格,两组输出之间无空行。 输入范例 : 2 1 100 输出范例 : 2 1
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

这个里面有一个无可避免的要点:大数乘法,涉及到手动乘法和加法的过程和原理,还有阶乘结果的存储方式。我这里用的的数组,也就是数组的每一个元素存放阶乘结果的每一位。

代码:

/*
	T48 求N! 
*/ 

#include<stdio.h>
#include<string.h>
#define MAX_SIZE 35700 // 10000的阶乘有35660位 

int res[MAX_SIZE];// 存放大整数乘法的结果 
void bigNumMulti(int n);

int main() {
	int N = 0;
	int i = 0;
	
	while (scanf("%d", &N) != EOF) {
		memset(res, -1, sizeof(res));// 重置结果 
		
		for (i = 1; i <= N; i++) {
			bigNumMulti(i);
		} 
		
		for (i = 0; i < MAX_SIZE; i++) {// 输出结果 
			if (res[i] != -1) {
				printf("%d", res[i]);
			}
		}
		printf("\n");
	}
	
	return 0;
} 

// 计算res表示的大整数与n的乘积
void bigNumMulti(int n) {// 15!开始就有问题了 
	int copyRes[MAX_SIZE] = {0};// 存放res的副本 
	int addArr[MAX_SIZE];// 存放计算乘法时中间的加数 
	int addCarry = 0, multiCarry = 0;// 加法的进位,乘法的进位
	int i = MAX_SIZE - 1, j = 0; 
	int digit = 0;// n的各个位数字 
	int multiTemp = 0;// 大整数与n的各个位相乘的中间结果 
	int addTemp = 0;// 加法的中间结果 
	int multiCount = 0;// 记录已经做了几次乘法 
	int nTemp = n; 
	
	if (res[MAX_SIZE - 1] != -1) {
		for (i = 0; i < MAX_SIZE; i++)// 更新副本(有结果的情况才需要更新) 
			copyRes[i] = res[i];	
		memset(addArr, -1, sizeof(addArr));// 将原结果清空			
	}
	else {// 初始情况,也就是n=1进来的时候 
		memset(copyRes, -1, sizeof(copyRes)); 
		copyRes[MAX_SIZE - 1] = 1;
		res[MAX_SIZE - 1] = 0;
	}
	memset(addArr, -1, sizeof(addArr)); 
	
	while (n) {// n的各个位与大整数的乘积 
		digit = n % 10;
		addCarry = 0, multiCarry = 0;// 重置进位
		 
		// 乘法原理:一个整数的各个位(设为a)与一个数字digit相乘的结果为 
		// a乘上digit加上上一个进位
		for (i = MAX_SIZE - 1; copyRes[i] != -1; i--) {
			multiTemp = copyRes[i] * digit + multiCarry; 
			addArr[i] = multiTemp % 10; 
			multiCarry = multiTemp / 10;// 更新进位 
		} 
		if (multiCarry > 0) {// 处理溢出的进位 
			addArr[i] = multiCarry;
		}
		
		// 加法原理:一个整数的各个位(设为a)与另一个整数的对应位(设为b)
		// 相加,结果为a加上b再加上上一个进位 
		for (i = MAX_SIZE - 1; addArr[i] != -1; i--) {
			if (multiCount == 0) {// 个位数,只做一次乘法的情况 
				res[i] = addArr[i];
			}
			else {// 做多次乘法的情况 
				if (res[i - multiCount] == -1)// 处理上一个加数越位的-1,为下一次加法做准备 
					res[i - multiCount] = 0;
				addTemp = res[i - multiCount] + addArr[i] + addCarry;
				res[i - multiCount] = addTemp % 10;
			}	
			addCarry = addTemp / 10;// 更新进位 
		}
		if (addCarry > 0) {// 处理溢出的进位,注意依然是i - multiCount 
			//错误写法!:res[i] = addCarry;这样就把结果的第2位覆盖了 
			res[i - multiCount] = addCarry;
		}
		
		memset(addArr, -1, sizeof(addArr));// 清空加数数组 
		multiCount++;// 乘法次数加1 
		n /= 10;
	}
	
}

感觉这里头细节太多了,稍不留神就会写错……不过好在用的原理没错

从这个题我学到了:

  • 理解了一套算法的原理(比如这个题里面大整数乘法用到的手动乘法和加法的原理),最后总会实现,虽然中间可能会有很多地方注意不到
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值