剑指offer--剪绳子

给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

原文链接:https://blog.csdn.net/u012429555/article/details/83184146

动态规划法:
     * 动态规划求解问题的四个特征: 
            ①求一个问题的最优解(最大,最小); 
            ②整体的问题的最优解是依赖于各个子问题的最优解; 
            ③小问题之间还有相互重叠的更小的子问题; 
            ④从上往下分析问题,从下往上求解问题

 

分析:
     有一段长度为n的绳子,我们现在要剪第一刀,我可以选择下第一刀的地方有1~n-1这些地方;比如长度为10的绳子,我第一刀可以在1~9这些地方下刀,共9种方式。
     第一刀下去后,绳子分成两部分,假设在i处下刀,绳子两部分就分别为:[0~i]与[i~n],长度分为表示为i与n-i;那么找出第一刀最合适的位置,其实就是找i在哪下刀,可以使得[0~i]与[i~n]的乘积最大,函数表示为f(n)=max(f(i)×f(n−i))。
     那么如何判断i处切最大呢?这个时候,我们就要知道,[0~i]这个长度的绳子,任意方式切,最大的乘积是多少;假如说,当我们要切一个长度为10的绳子:切成1和9与4和6,两种方式,哪个乘积更大? 回答:不光要考虑第一刀后两个绳子的大小,还要考虑到9、4、6这三种情况,因为第一刀切出的绳子长度是否可以再切第二刀,使它有更大的乘积,比如将9再切成 3×3×3,6切成 4×2,哪个更大?
     这种情况下,我们可以发现,无论再怎么切,一定是越切越短,那么我们是否可以将小于给定长度的绳子的每一个长度的最大乘积都求出来?  即:长度为10的绳子,我们就计算出:长度1~9这9种长度的绳子,每种长度的最大乘积是多少。 要求长度9的绳子的最大乘积,我们要知道1~8各个长度的最大乘积,要知道长度8的最大乘积,就要知道1~7长度的各个最大乘积,以此类推。

f(n)定义为将长度为n的绳子分成若干段后的各段长度的最大乘积(最优解),在剪第一刀时有n-1种剪法,可选择在0 < i < n处下刀;

 在i处下刀,分成长度为i的左半绳子和长度为n-i的右半绳子,对于这两根绳子,定义最优解为f(i)和f(n-i),

于是f(n) = max(f(i) * f(n-i)),即求出各种相乘可能中的最大值就是f(n)的最优解

 就这样从上到下的分下去,但是问题的解决从下到上。即先求出f(2)、f(3)的最优解,然后根据这两个值求出f(4)、f(5)...直到f(n)
     * f(2) = 1,因为只能分成两半
     * f(3) = 2,因为分成两段2*1 大于分成三段的1*1*1

 public static int maxProductAfterCutting(int length){
        //长度小于2 无法分割
        if(length<2)
            return 0;
        //长度等于2 一分为二 1*1
        if(length==2)
            return 1;
        //长度等于3 最大为1*2=2
        if(length==3)
            return 2;
         //子问题的最优解存储在products数组中,数组中的第i个元素表示把长度为/i的绳子剪成若干段    
         //后各段长度乘积的最大值。
        int[] products=new int[length+1];
        //这些情况下,不剪的时候长度比剪的时候长,所以作为初始条件
        products[1]=1;
        products[2]=2;
        products[3]=3;
        for(int i=4;i<=length;i++) {
            int maxModify=0;
            for(int j=1;j<=i/2;j++) {
                int product=products[j]*products[i-j];
                if(product>maxModify)
                    maxModify=product;
            }    
            //得到f(i)的最优解
            products[i]=maxModify;
        }    
        //返回发f(n)
        return products[length];
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值