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;
}
}
感觉这里头细节太多了,稍不留神就会写错……不过好在用的原理没错
从这个题我学到了:
- 理解了一套算法的原理(比如这个题里面大整数乘法用到的手动乘法和加法的原理),最后总会实现,虽然中间可能会有很多地方注意不到