剑指 Offer 14 剪绳子

题目:一段绳子长度为n(n>1),至少剪一次,问每段长度的乘积的最大值是多少?

public int cuttingRope(int n)

思路:当n=2,3,4,时单独分析,结果分别是1,2,4。
当n>=5时,2(n-2)>n,3(n-3)>n。意思是当n大于等于5时,剪一段为2,另一段为n-2,则它们的乘积更大。而3(n-3)>=2(n-2)所以应该尽量地以每段为3来剪。

实现为:n=3a+b; a=n/3; b=n%3 也就是有a段3。b只有0,1,2三种情况。当b=0时,结果为3a(用Math.pow)。当b=1时,结果为3(a-1)乘4,这是把最后的b=1和一个3弄成了2*2,变大,再加上前面的。当b=2时,结果为3a*2。

	//没有大数运算的话,直接用n=3a+b,时间复杂度为O(1)
    //Math.pow(a,b),a b为实数,时间复杂度为O(1)
	public int cuttingRope(int n){
        if(n <= 1){
            return -999;
        }else if(n <= 3){
            return n-1;
        }else if(n == 4){
            return 4;
        }
        int a = n / 3;
        int b = n % 3;
        if(b == 0){
            return (int)Math.pow(3,a);
        }else if(b == 1){
            return (int)Math.pow(3,a-1) * 4;
        }else{
            return (int)Math.pow(3,a) * 2;
        }
    }

剪绳子(大数运算)
题目相同,但是当n取很大时,分的段数a可能会导致指数3^a过大。

public int cuttingRope(int n)

先给面试官讲O(n)的while解法,再讲O(logn)的快速幂(指数二进制表示,最右端指针向左移动,求3^(n-1))的解法。

思路:1.使用while处理,n每次减3,直到小于5就结束。并说明正确性。时间复杂度:O(n)

正确性保证:n不会减到1,因为当n减到4时就结束了。当最后n=2,3,4时都满足条件。

	//有大数运算,用while,并解释正确性,O(n)
    public int cuttingRopeWithBigNum(int n){
        if(n <= 1){
            return -999;
        }else if(n <= 3){
            return n-1;
        }else if(n == 4){
            return 4;
        }
        long res = 1;
        //n等于4时结束,所以n不会为1。所以n最后为4,3,2都满足条件
        while(n > 4){
            n -= 3;
            res *= 3;
            res %= 1000000007;
        }
        //注意最后把n乘上
        return (int)((res*n)%1000000007);
    }

2.快速幂的方法。
求a^b,把b用二进制分解,次数分成和的形式(一个初始指针在最右边,次数除2,指针就向左移动一格)。这样次数b可以一半的速度减小,就类似二分的思想,时间复杂度为O(logn)
比如求a^10,10的二进制为1010,当次数为10时,它不是奇数,所以最右边那位为0。x则等于x*x。这样随着次数不断减小,x作为系数则正好吻合。这样当次数为0时就得到了结果。(这个过程就相当于初始一个指针在最右边,次数除2,就向左边移动一格。指针所指为1,也就是次数为奇数,最后的结果res就乘上不断变化且吻合的x系数)

注意:快速幂只能先求3(n-1)。而不能先求3^n,因为当b==1时要先除3再乘4,或者当多乘一个3后取余1000000007后就不是3的倍数了,再除3再乘4答案就不对了。
而先求3^(n-1)后,b的三种情况都直接乘一个数,所以不会出问题。

public int cuttingRopeWithBigNumByFastPower(int n){
        if(n <= 1){
            return -999;
        }else if(n <= 3){
            return n-1;
        }else if(n == 4){
            return 4;
        }
        int a = n / 3 - 1;
        int b = n % 3;
        long res = 1,x = 3;
        //快速幂求3^(a-1)
        //把a次幂用二进制拆开,通过a每次右移一位来看,来看它在这二进制位上是否为1,并用x从右往左计算
        //用于后面res的相乘
        //不能求3^a,是因为b除3再乘4(当b==1时),但是由于%了1000000007,所以算完不是3的倍数,所以会导致错误
        //所以最后都弄成乘法就不会出错
        while(a > 0){
            if((a & 1) == 1) {
                res = (res * x) % 1000000007;
            }
            x = (x * x) % 1000000007;
            //除2是往右移动1位
            a >>= 1;
        }
        if(b == 0){
            return (int)(res * 3 % 1000000007);
        }else if(b == 1){
            return (int)((res * 4) % 1000000007);
        }else{
            return (int)((res * 6) % 1000000007);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值