剑指 Offer 14- I. 剪绳子&&剑指 Offer 14- II. 剪绳子 II(动态规划+贪心算法)

剑指 Offer 14- I. 剪绳子

题目描述

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

II:上面题目一样,但是答案需要取模1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof

解题思路(动态规划)

比如在拆分3的时候,我们可以先拆分为1和2,得到一个乘积,然后把3分为1,1,1又得到一个乘积,我们再比较这两个乘积就好了

如果n=4,第一次切绳子可以满足的切法有(1,3)(2,2),我们后续的切法肯定是基于这两种切法中的较大值进行切分,很明显(2,2)更大,那我们还需要切吗?如果要切,只能对长度为2的绳子进行切割,然而2切了的话,最大乘积是1×1=1,何必呢?,还不如不切,因为2×2>1×1×2,那么(2,2)不再切呢,得到的乘积是4,所以n=4,最大乘积为4

现在如果n=5,
第一刀可以切成(1,4),(2,3),现在就变成了长度为1和4的绳子能切出来的乘积与长度为2和3的绳子能切出来的乘积作比较,看谁大,我们上面分析过,长度为4的绳子切出来最大乘积为4,那么(1,4)的最大值应该是1×4=4;
(2,3)的最大值为2×3=6;
所以,n=5,最大乘积为6;

我们求DP[5]切第一刀是不是可以先比较这两种切法,看哪个大,接下来,再求DP[2]和DP[3]这就是分成子问题

在这里插入图片描述
现在看n=6;有3种组合(1,5)(2,4)(3,3)
(1,5)可以对5进行切割,我们已经知道对5进行切割最大值是6,那么第一次切成(1,5)这种方案的最大值是1×6=6;
(2,4)可以对2和4进行切割,我们已经知道对4进行切割最大值是4,那么第一次切成(2,4)这种方案的最大值是2×4=8;
(3,3)可以对3和3进行切割,我们已经知道对3进行切割最大值是3(不切割),那么第一次切成(3,3)这种方案的最大值是3×3=9;
所以n=6,最大乘积为9;

我自己的写法(动态规划)

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        if(n==1)
            return 0;
        if(n==2)
             return 1;
        if(n==3)
             return 2;
        int dp[]=new int[n+1];//因为最后一次要用到dp[n]
       // dp[0]=0;//默认为0,不用写
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;//n都大于等于4了,如果切出来有一块长度为3,那这个3就不用切了啊(3>2),上面n=3返回2是因为这段绳子必须切断
        int max=0;
        int val=1;
        for(int i=4;i<=n;i++)
        {
    
                //max=0;
            for(int j=1;j<=i/2;j++)
            {
    
    
            //j<=i/2是因为我们每次取两个值来做乘法,比如i=4,就只用算j=1,j=2的情况,因为j=3的话 val=dp[3]*dp[1],j=1时,val=dp[1]*dp[3],这就是一样的啊
                //max=Math.Max()
                 val=dp[j]*dp[i-j];
                 max=max>val?max:val;//max取max和val中的最大值
            }
            dp[i]=max;//算出来了每一个i对应最大值,记得更新dp[i]的值啊,这样下次程序好直接取
        }      
        return max;
    }
}

在这里插入图片描述

视频中的解法(更精简)

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        if(n==1)
            return 0;
        if(n==2)
             return 1;
        if(n==3)
             return 2;
        int dp[]=new int[n+1];
       // dp[0]=0;//默认为0,不用写
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;//n都大于等于4了,如果切出来有一块长度为3,那这个3就不用切了啊(3>2),上面n=3返回2是因为这段绳子必须切断        
        for(int i=4;i<=n;i++)
        {
    
               
            for(int j=1;j<=i/2;j++)
            {
    
    
            //j<=i/2是因为我们每次取两个值来做乘法,比如i=4,就只用算j=1,j=2的情况,因为j=3的话 val=dp[3]*dp[1],j=1时,val=dp[1]*dp[3],这就是一样的啊                       
                 dp[i]= Math.max(dp[i],dp[j]*dp[i-j]);
            }          
        }      
        return dp[n];
    }
}

这个内存消耗是非常扯淡的,每次运行不一样,35.4M只能超过18%
在这里插入图片描述

贪心算法

算法面试5leetcode剪绳子动归贪婪

首先有两个推论,
① 当所有绳段长度相等时,乘积最大。
② 最优的绳段长度为 3 。
参考https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/

在这里插入图片描述

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        if(n <= 3) return n - 1;
        int a = n / 3, b = n % 3;
        if(b == 0) return (int)Math.pow(3, a);
        if(b == 1) return (int)Math.pow(3, a - 1) * 4;//(剩了个1,就让它和3结合起来组成4)
        return (int)Math.pow(3, a) * 2;//2*3>5哦,所以不结合
    }
}

在这里插入图片描述

public int cuttingRope(int n) {
    
    
    if (n == 2 || n == 3)
        return n - 1;
    int res = 1;
    while (n > 4) {
    
    
        //如果n大于4,我们不停的让他减去3
        n = n - 3;
        //计算每段的乘积
        res = res * 3;
    }
    return n * res;
}


作者:sdwwld
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/shu-xue-zhi-shi-he-dong-tai-gui-hua-liang-chong-fa/
public int cuttingRope(int n) {
    
    
    if (n == 2 || n == 3)
        return n - 1;
    else if (n % 3 == 0) {
    
    
        //如果n是3的倍数,绳子全部剪为3
        return (int) Math.pow(3, n / 3);
    } else if (n % 3 == 1) {
    
    
        //如果n对3求余等于1,我们剪出一个长度为4的,其他长度都是3
        return 4 * (int) Math.pow(3, (n - 4) / 3);
    } else {
    
    
        //如果n对3求余等于2,我们剪出一个长度为2的,其他长度都是3
        return 2 * (int) Math.pow(3, n / 3);
    }
}


作者:sdwwld
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/shu-xue-zhi-shi-he-dong-tai-gui-hua-liang-chong-fa/

剑指 Offer 14- II. 剪绳子 II

在这里插入图片描述

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        if(n == 2) {
    
    
            return 1;
        }
        if(n == 3){
    
    
            return 2;
        }
        int mod = (int)1e9 + 7;
        long res = 1;
        while(n > 4) {
    
    
            res *= 3;
            res %= mod;
            n -= 3;
        }
        return (int)(res * n % mod);
    }
}


作者:HenryLee4
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/javatan-xin-si-lu-jiang-jie-by-henrylee4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在这里插入图片描述

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        if(n <= 3) return n - 1;
        int b = n % 3, p = 1000000007;
        long rem = 1, x = 3;
        for(int a = n / 3 - 1; a > 0; a /= 2) {
    
    
            if(a % 2 == 1) rem = (rem * x) % p;
            x = (x * x) % p;
        }
        if(b == 0) return (int)(rem * 3 % p);
        if(b == 1) return (int)(rem * 4 % p);
        return (int)(rem * 6 % p);
    }
}


作者:jyd
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在这里插入图片描述

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        if(n <= 3) return n - 1;
        long res=1L;
        int p=(int)1e9+7;
        //贪心算法,优先切三,其次切二
        while(n>4){
    
    
            res=res*3%p;
            n-=3;
        }
        //出来循环只有三种情况,分别是n=2、3、4
        return (int)(res*n%p);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ningmengshuxiawo/article/details/113206306