剑指offer-面试题14:剪绳子 动态规划+贪心算法

题目描述

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

输入: 10
输出: 36
解释: 10=3+3+4,3x3x4=36

动态规划解法

建立并维护一个长度为n+1(因为数组的下标从0开始)的一维表dp,dp[i]记录长度为i的绳子被剪成m(m>1)段后各段相乘的最大乘积。从长度为2开始遍历,当绳长为i时,我们可以尝试先剪下来长度为j的一段(j>=1 && j<i;再让j和剩下的长度为i-j或dp[i-j]两者中较大的相乘(即i-j在这里可以被剪开,也可以不剪),得到一个乘积temp(i,j);选择所有这样的乘积中最大的,就是dp[i]的值。

状态转移方程:

dp[i]=max(max(j*(i-j),j*dp[i-j]))            // j>=1 && j<i

动态规划题解代码如下:

import java.lang.Math;
class Solution {
    public int cuttingRope(int n)
    {
        int[] dp=new int[n+1]; //用于动规的记录表,dp[i]存储长度为i的绳子剪成若干段后的最大乘积
        for(int i=2;i<n+1;i++)
        {
            for(int j=1;j<i;j++)
            {
                int temp=Math.max(j*dp[i-j],j*(i-j));
                dp[i]=Math.max(dp[i],temp);
            }
        }
        return dp[n];
    }
}

时间复杂度:O(n2)
空间复杂度:O(n)

贪心解法

当求解每个子问题时,都有一个确定的策略,使用这个策略一定能得出子问题的最优解,进而求得整个问题的最优解,我们便可以使用贪心算法。采用贪心策略一般需要在数学上证明它的正确性

对于本题,我们可以使用以下的策略剪绳子:当n<=5时,剪绳子最大乘积易求;当n>5时,我们不断地剪下长度为3的绳子段,直到剩下的绳子长度小于等于5

接下来证明贪心策略的正确性:当n>5时,一定有 2(n-2)>n 和 3(n-3)>n,另外还有3(n-3)>2(n-2)。因此剩余绳长大于5时,我们要不断剪下长度为3的绳段。

import java.lang.Math;
class Solution {
    //基于贪心算法
    public int cuttingRope(int n)
    {
        int[] dp={0,0,1,2,4,6};
        if(n<=5)
        {
            return dp[n];
        }
        int b=n/3,y=n-(b-1)*3;
        if(y==2)
        {
            y=5;
        }
        return (int)(Math.pow(3,b-1))*Math.max(y,dp[y]);
    }
}

在代码中,我们不需要对绳子长度进行循环的减3操作,只需要用算术方法求出剩余长度小于5时可以剪下的最多次数和剩余长度,时间和空间复杂度均为常数
时间复杂度:O(1)
空间复杂度:O(1)

发布了9 篇原创文章 · 获赞 0 · 访问量 210

猜你喜欢

转载自blog.csdn.net/qq_24210431/article/details/104501016