最开始的思路很简单,设一个循环变量从1-n,依次乘到累乘变量s中,只保留s的最后几位非零数字,结果hll的错了,原因主要有两个:1是速度慢,2是在乘的过程中会碰到5的倍数,而每乘一个5的倍数,末尾就会产生一个零,为了保持最后几位都是非零数字,就需要整体右移,这样一来最高位的数字就变得不精确,经过多次右移后,s就完全不是精确的结果了,当然没法得到答案.
这个思路被断掉后一时没了想法,最后多亏老马的提示:末尾0产生的原因是乘式中有2*5这样的因子,因此在计算的时候就该先将2,5因子成对剔除,这样在计算时只需要保留最后一位就不会产生精度问题了.然后老马下面的思路让我崇拜至极(我根据实现的需要做了些许改动):将整个阶乘的算式1*2*3*....n每10位一段进行分组,也就是1*2*3*..10一组,11*12*13*...20一组,如此分下去得到n/10组,令k=n/10.然后将每组中剔除掉所有因子里面的2个5和2个2,这样再将剩下的所有组相乘即可.当然,分组的目的不在于剔除,而是可以发现在每组中,除去xxxx5和xxx10,剩下的数字相乘再除以4的最后一位都是4,而4的k次方的最后一位可以构成一个周期序列4,6,4,6,4,6........ 而被剔除的5的倍数将每个都除以5后又得到了一个新的阶乘(n/5)!,你需要做的就是求出这个新阶乘的最后一位并于前面得到的4或6相乘再取最后一位. 当然,还不要忘记因为数量不够,没有凑成一组的最后几个数字.要求最后这几个数字乘积的最后一位就很简单了,最后一位相乘再取最后一位即可.当然,由于分组就是每10个一组,所以最后这些数的乘积事先打表记录就行,无需计算.但是注意,如果最后余下的数字里还有单个的xxxx5话,这个数字已经包含到(n/5)!里去算了,所以在这应该另外处理一下:方法是将xxxx4这个数字先除2,然后取出最后一位来再和其他数相乘.
思路就是这样,总的来说是一个递归过程,对n!的乘式进行分组,设函数solve(n)就是求n!的最后一位.那么例如26!=1*2*3*4*5*......26,进行如下分组,每组用[]括起: 26!=[(1*2*3*4*6*7*8*9)*(11*12*13*14*16*17*18*19)] * [(21*22*23*24*26)] * [(5*10*15*20*25)] [(1*2*3*4*6*7*8*9)*(11*12*13*14*16*17*18*19)]中,每个()里的算式再除以4得到的结果的最后一位都是4,所以可以简化成[4^2],取最后一位,记做A [(21*22*23*24*26)]中,将24/2后与其他数相乘,取最后一位,记做B [(5*10*15*20*25)],将其中每个数都除以5,再相乘后的最后一位恰是solve(5), 所以solve(26)是A*B*solve(5)的最后一位.
兴致勃勃的实现后提交WA掉了....无比郁闷,但也没检出错,只好上网搜解题报告,才发现原来这个题目的数据范围n超出了int的范围,达到了令人发指的10^100!!!只好又改成用字符串存储,并将需要的整数算法用字符串算法代替,结果提示access violation,看来数字的大小不止10^100,直接将数组开的1000,提交,AC. 代码如下:
01
#include<iostream>
02
#include<cstring>
03
#include<fstream>
04
#include<vector>
05
using
namespace
std;
06
07
#define f(i,a,b) for(i=a;i<=b;i++)
08
09
int
head
[
2
]
=
{
6
,
4
};
10
int
a
[
10
]
=
{
1
,
1
,
2
,
6
,
4
,
6
,
6
,
2
,
6
,
4
};
11
12
int
solve(
char
* n)
13
{
14
int
l
=
strlen(n
),
m
= n
[
l
-
1
]
-
'0'
,
tail;
15
if (
m
<
5)
16
tail
=
a
[
m
];
17
else
18
tail
= (
a
[
m
]
*( (((n
[
l
-
2
]
-
'0')
&
1
!=
0)
&&(
l
>
1))
?
7
:
2))
%
10;
19
if (
l
==
1)
20
return
tail;
21
else
22
{
23
char
n1
[
1005
];
24
register
int
i;
25
int
k
= (n
[
0
]
>
'4'
?
1
:
0);
26
n1
[
0
]
=
'1';
27
f(
i
,
k
,
l
-
2
+
k)
28
n1
[
i
]
= (n
[
i
-
k
]
-
'0')
*
2
%
10
+ (n
[
i
+
1
-
k
]
>
'4'
?
1
:
0)
+
'0';
29
n1
[
i
]
=
'/0';
30
return (
head
[(n
[
l
-
2
]
-
'0')
&
1
]
*
tail
*
solve(
n1))
%
10;
31
}
32
}
33
int
main()
34
{
35
char n
[
1005
];
36
while(
gets(n))
37
cout
<<
solve(n)
<<
endl;
38
return
0;
39
}
最后非常感谢老马提供的思路和张霄学长热情的指导,祝学长实习顺利~~~
02 #include<cstring>
03 #include<fstream>
04 #include<vector>
05 using namespace std;
06
07 #define f(i,a,b) for(i=a;i<=b;i++)
08
09 int head [ 2 ] = { 6 , 4 };
10 int a [ 10 ] = { 1 , 1 , 2 , 6 , 4 , 6 , 6 , 2 , 6 , 4 };
11
12 int solve( char * n)
13 {
14 int l = strlen(n ), m = n [ l - 1 ] - '0' , tail;
15 if ( m < 5)
16 tail = a [ m ];
17 else
18 tail = ( a [ m ] *( (((n [ l - 2 ] - '0') & 1 != 0) &&( l > 1)) ? 7 : 2)) % 10;
19 if ( l == 1)
20 return tail;
21 else
22 {
23 char n1 [ 1005 ];
24 register int i;
25 int k = (n [ 0 ] > '4' ? 1 : 0);
26 n1 [ 0 ] = '1';
27 f( i , k , l - 2 + k)
28 n1 [ i ] = (n [ i - k ] - '0') * 2 % 10 + (n [ i + 1 - k ] > '4' ? 1 : 0) + '0';
29 n1 [ i ] = '/0';
30 return ( head [(n [ l - 2 ] - '0') & 1 ] * tail * solve( n1)) % 10;
31 }
32 }
33 int main()
34 {
35 char n [ 1005 ];
36 while( gets(n))
37 cout << solve(n) << endl;
38 return 0;
39 }