剑指 Offer 14.剪绳子(动态规划、数学分析)

一、题目内容

        剑指 Offer 14.剪绳子(动态规划、数学分析)_第1张图片

二、题目分析

        这道题目讲道理,我看到的第一眼就是动态规划,但是后来提交之后,发现还有大佬考虑用数学分析得出精简解法,在这里我也会一 一阐述。

        对于动态规划而言,按照老套路,首先定义dp数组,含义明了,定义长度为n+1的dp数组,其中dp[i]代表长度为i的绳子的最大乘积

        其次,我们寻找初始条件,简单分析就可以得到,dp[2]=1,dp[1]=1(虽然题目告诉我们n>=2,但是设置dp[1]有利于接下来的计算),好的我们得到了初始条件。

        接着我们需要考虑状态转移方程,对于本题而言,这里是最困难的部分。怎么把dp[i]和dp[i-1]联系在一起呢?我们发现这是不可能的,为什么?

        因为我们容易知道dp[3]等于2,是指将长度为3的绳子分成1和2或者2和1,相当于将长度为3的绳子从中间切了一刀,而dp[4]的值应该是2,是将长度为4的绳子分成两个长度为2的绳子。那么大家想一想,我怎么可以从dp[3]得到dp[4]呢?总不能把dp[3]切开的部分粘合起来吧?所以,如果用dp[3]计算dp[4],得到的结果应该是1*2*1,也就是dp[3]*(4-3),它的意思是,当长度为3的绳子分成1和2之后,又来了一个长度为1的绳子,没办法,总不能把你们拼接起来,那只能分成三份了1*2*1,得到2,由此我们可以看出,虽然通过dp[i-1]得到了一个dp[i],但是它远不是最优解

        那么咋办呢?我们可以想到,既然dp[i-1]得到的不是最优解,那么总有方式得到最优解吧。

所以,我们可以在定义i从3到n的大循环内,再定义一个从0到i(左闭右开)的小循环,目的就是将dp[i-j]看成一个整体,将j看做一个整体,这样就是计算两段绳子的乘积最大值,举个例子:

        dp[2]等于1,是指将长度为2的绳子分成了1和1,而dp[3]呢?我们从0开始,依次计算dp[i-j]*j,当j=0时,dp[i-j]*j=0;当j=1时,dp[i-j]*j=1,指的是将长度为2,分成1和1的那段绳子看做一个整体,然后多出来的1又做一个整体,这样得出dp[3]=1,不是最优解,么儿关系,继续看;当j=2的时候,dp[i-j]*j=2,指的是将长度为1的绳子看做一个整体,然后多出来的长度为2的绳子又看做一个整体,这里注意,这时候,不要一直关注于刚刚所说的将dp[2]分为1和1,此时是dp[1],只有一个长度为1的绳子,然后多出来的是长度为2的一个长绳子,这样就的出来最优值dp[3]=2了。

        但是其实还有个问题,应该dp[i]=dp[i-j]*j并不能包含所有情况,比如dp[4]你会发现,不管怎么遍历,都只能得出3,这是因为当我们让dp[4]=dp[4-2]*2时,发现结果为1*2,,而不是我们想象的2*2,这是因为呀,dp[2]是将长度为2 的绳子分成了1和1,那么dp[4]就是将长度为4的绳子分为了1*1*2,自然不是最优解啦,这里不是最优解的根源在于,明明分成两段就可以解决的事,非要分成三段,那我们不如再考虑一下所有分成两份的情况,也就是(i-j)*j,即将所有长度为i的绳子,j从0到i,都分成(i-j)和j的绳子,这样就可以解决那种只要分成两份,而不会分成三份的情况啦。

         所以最终我们的状态转移方程就是:

        

dp[i]=max(dp[i],dp[i-j]*j,(i-j)*j);

        所以完整代码为:

 public int cuttingRope(int n) {
        int []dp=new int [n+1];
        dp[1]=1;
        dp[2]=1;
        for(int i=3;i<=n;i++){
            for(int j=0;j

三、数学分析

        这道题归类在数学题中,说明可以经过独到的数学分析得出最优的结果。

        力扣上的解析讲的很好,我就不班门弄斧了:数学分析解决剪绳子问题

                                                                                                                —— END SUB

你可能感兴趣的:(动态规划,leetcode,算法,职场和发展)