UVa568:Just the Facts
题目大意
给定一个数字n,求n!的最后一位非零数字。
例:当n为5的时候,n!为120,所以答案是2;
当n为7的时候,n!为5040,所以答案为4。
Solution
因为阶乘可能会非常大并且最后一位非零数字和前面的数字无关,所以很直观的想法是只保留非零的、最后的若干位数字。
所以关键就是要保留多少位的问题,最开始我只保留了一位,但很快就出现问题了。比如说保留的数字是2,下一个需要乘起来的数字是15,这样就会得到30,除去末尾的0就得到了3,而显然,这里的答案是有问题的,正确答案应该和更高位的数字有关,而这个数字被我们舍弃了。
经过我的几番尝试,发现保留5位数字可以得到正确的答案。逆推了一下原因,因为本题中最大的n是10000,所以与一位数字相乘能得到的最大数字是90000,保留五位数字就可以保证所有关键信息不被舍弃。
而理论上应该是保留位数越多越好,因此如果保留6位或者更多位数的话应该依然会得到正确结果,但我试了一下,发现错了。原因是,我采用int存储数据,如果保留的数字超过5位,在进行乘法的时候就有可能会造成溢出,所以5位数字就成了一个本题中的magic number(当然,如果采用long long存储数据也可以规避上述溢出问题)。
AC-Code(C++)
Time:0ms
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <climits>
#include <ctime>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const int maxn = 10000 + 10;
int dp[maxn];
int fact(int x){
if(dp[x] != -1)
return dp[x];
dp[x] = x * fact(x-1);
while(dp[x] % 10 == 0)
dp[x] /= 10;
dp[x] %= 100000;// a magic number....
// 10 100 1000 10000 are not big enough, since the bigest n can be 10000
// 1000000 is too large can result in overflow
return dp[x];
}
int main(int argc, const char * argv[]) {
// freopen("input.txt", "r", stdin);
int n;
memset(dp, -1, sizeof(dp));
dp[0] = 1;
while(scanf("%d",&n)==1){
printf("%5d -> %d
",n,fact(n)%10);
}
return 0;
}