小史是一个应届生,虽然学的是电子专业,但是自己业余时间每天都会坚持学习编程、计算机、互联网等相关方面的知识,特别是算法的训练。
今天小史又在研究一道算法题了。
【学习现场】
小史今天学习了阶乘。
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。
亦即n!=1×2×3×...×n。阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n。
同时,小史也碰到一个问题,要编程求出20181124的阶乘末尾有多少个0。
题目:如何求取20181124的阶乘末尾有多少个0?
题目要求:请编程计算出结果,如果数字过大,给出它对100007取模之后的结果。
小史拿到题目后略微思考,很快有了思路,开始写代码了。读者们,你能比小史更快吗?
学习现场的玩法:
1)计算机编程解决题目,答案在公众号后台进行回复,回复格式为解答+答案,如答案为2,则回复解答2
2)回答正确后,会出现提示,同时有一个交流群的二维码,可进入“场粉交流群”,群内都是算法爱好者和互联网从业人士
3)注意,每道题都有有效期,有效期过后题目作废,只能等待下一道题了
4)学习现场是小史平时练习的题目,下一道题公布的时候,同时会公布上一道题小史的解法供大家参考,也欢迎大家将自己的解法进行留言
5)希望大家玩得开心,这也是最重要的一点
上期回顾:
小史解答:
本题可用动态规划,不了解什么是动态规划的同学可看
1)由于n确定之后,n拆分之后的最大乘积也就确定,所以可以把n拆分之后的最大乘积定义为状态。
2)状态转移:n拆分后的最大乘积 = max ( n-1拆分之后的最大乘积 x 1 , n-2拆分之后的最大乘积 x 2 , n-3拆分之后的最大乘积 x 3 , …… , 1拆分之后的最大乘积 x (n-1))
3)用循环就能计算出100拆分之后的最大乘积,注意,乘积有点大,要用long存储
想清楚了算法细节之后,小史的代码写起来也是非常快,不一会儿就写好了:
MaxProduct.java
/**
* @author xiaoshi on 2018/11/12.
*/
public class MaxProduct {
// 中间状态
private long[] dp = null;
// 计算中间状态
private void calcStatus(int n) {
// 从前往后
for(int i = 1; i <= n; i++) {
// 记录最大乘积
long max = 1L;
// 拆解
for(int j = 1; j < i; j++) {
// 拆出一个j,剩下的要么是(i-j),要么是之前算好的dp[i-j]
long product = Math.max(dp[i - j] * j, (i - j) * j);
// 更新最大值
if(product > max) {
max = product;
}
}
// 最大值保存
dp[i] = max;
}
}
// 入口方法
public long getMaxProduct(int n) {
// 初始化dp数组
dp = new long[n + 1];
dp[0] = dp[1] = 1L;
// 计算中间状态
calcStatus(n);
// 返回结果
return dp[n];
}
}
(友情提示:可左右滑动)
Main.java
/**
* @author xiaoshi on 2018/11/12.
*/
public class Main {
public static void main(String[] args) {
MaxProduct maxProduct = new MaxProduct();
long result = maxProduct.getMaxProduct(100);
System.out.println(result);
System.out.println(result % 100007);
}
}
(友情提示:可左右滑动)
运行结果
7412080755407364
71144
结束啦?==
如果数字再大一点,long也不够了,数据要溢出。
而且小史很好奇,100到底是怎么被拆分才得到了最大值?
小史断点运行程序,并且打印出一些必要的中间状态,发现100被拆分成了2个2和32个3。
数学方法:
小史用数学的方法仔细分析了一下这个问题,先从简单的入手
1,不用说,最大乘积为1
2,只能拆为1+1,不拆还更好,最大乘积为2
3,只能拆为1+2 或者 1+1+1,不拆还更好,最大乘积为3
4,可以拆为2+2,最大乘积为4
5,可以拆为2+3,最大乘积为6
6,可以拆为2+2+2,也可以3+3,最大乘积为9,这里可知2个3比3个2更好
7,可以拆为3+2+2,最大乘积为12
8,可以拆为2+2+2+2,但是由6知,2个3比3个2更好,所以3个2换成2个3,拆为2+3+3,最大乘积为18
以此类推。
由以上过程可以得到几个结论。
一、4之后,数字拆分得到的最大乘积已经比数字本身要大,所以4之后的数字肯定要拆分。
二、由于2个3比3个2更好,所以尽量拆分出3。
三、一定不要拆分出1,因为白白浪费了一个数。
再进一步,根据尽量拆分出3和不要拆分出1的原则,可以得到以下推论(这里讨论n>4的情况):
如果 n % 3 == 0,最大乘积就是3^(n/3)
如果 n % 3 == 1,最大乘积就是3^((n-4)/3) x 4
如果 n % 3 == 2,最大乘积就是3^((n-2)/3) x 2
有了这个通项公式,再结合快速幂算法和取模特性,可以再log(n)时间内计算出结果,也不用担心数据溢出了。
学习现场是互联网侦察继面试现场后推出的一个姊妹现场,旨在帮助读者进行算法题的训练,同时提供学习交流群,希望对大家的学习和算法提高有所帮助。