剑指 Offer 14. 剪绳子(343. 整数拆分)

题目:

【1】第一种【这种算是比较常规的,主要考察的是切分的思维

 

 

【2】第二种【这是第一种的变种,考察的是对数据类型范围的处理,因为int的范围不足以容纳下结果

 

 

思路:

【1】思路一:贪心算法,贪心算法是要经过数学推到得出最优值,如在计算乘积最大的时候,会发现优先长度是3的,乘积是最大的,所以就需要优先分长度为3。其次还会有一些特殊情况,如4,3,2。因为条件里面是必须剪一刀,故要进行特殊处理。

【2】思路二:动态规划,也就是一步步的推上去,从绳子长度为1,推到绳子长度为n的最大乘积。

【3】针对变种的情况更多的是考虑到对数据容量的思考,因为当容量变大了,其实int是不合适的,贪心的情况可以考虑long类型,然后取余强转换,但是对于动态规划显然是不行的,因为存在一层层的往上推,大数相乘很容易就会溢出long类型的范围,所以要采用BigInteger类型。

代码展示:

public class Offer {
    public static void main(String[] args) {
        Random random = new Random();
        int target = random.nextInt(57)+2;
        System.out.println(Method4(120));
    }


    //思路一:贪心算法
    // 基于m>1,所以表明了至少要剪一刀
    public static int Method1(int n){
        //由于n>1,所以不考虑绳子长度为1的情况,考虑2返回1,3返回2,因为必须剪一下
        if (n <= 3){
            return n-1;
        }
        if (n == 4){
            return 4;
        }
        int result = 1;
        while (n > 4){
            result *= 3;
            n -= 3;
        }
        //因为按3剪最后可能会剩下2或1,2要补上
        return result*n;
    }

    //在变种的情况下,也就是将输入值的范围扩大为 2 <= n <= 1000
    //而int: 32位整数 -2,147,483,648——2,147,483,647
    //long: 64位整数 -9,223,372,036,854,775,808—— 9,223,372,036,854,775,807
    public static int Method2(int n){
        //由于n>1,所以不考虑绳子长度为1的情况,考虑2返回1,3返回2,因为必须剪一下
        if (n <= 3){
            return n-1;
        }
        if (n == 4){
            return 4;
        }
        //所以存在溢出的情况下就只能用long了
        long result = 1;
        long limit = 1000000007;
        while (n > 4){
            result = result*3%limit;
            n -= 3;
        }

        //因为按3剪最后可能会剩下2或1,2要补上,且要转回为int
        return (int)(result*n%limit);
    }
    //思路二:动态规划
    public static int Method3(int n){
        if (n <= 3){
            return n-1;
        }
        if (n == 4){
            return 4;
        }
        int[] dp = new int[n+1];
        //至于这里为什么是1,2,3?是因为这里是剪掉的部分的最大值,如5剪成2*3,那么3的部分最大值是3,因为3在不剪的情况下大于剪之后的,2也同理。
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for (int i = 4; i<=n;i++){
            //j<=i/2是因为1*3和3*1是一样的,没必要计算在内,只要计算到1*3和2*2就好了【减少计算量】
            for (int j = 1;j<= i/2;j++){
                //max(j*(i-j),j*dp[i-j]是由于减去第一段长度为j的绳子后,可以继续剪也可以不剪
                //如果不剪就是j*(i-j),如果继续剪那么dp[i-j]就是这部分的剪得效果的最大值
                //max(dp[i],Math.max(j*(i-j),j*dp[i-j]))是当j不同时,求出最大的dp[i]
                dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
            }
        }
        return dp[n];
    }

    //针对变种情况,动态规划其实变动不用很大,就是将类型改一下
    //但是你会发现int和long其实都会存在溢出的问题,所以改用BigInteger
    public static int Method4(int n){
        if (n <= 3){
            return n-1;
        }
        if (n == 4){
            return 4;
        }
        BigInteger dp[] = new BigInteger[n + 1];
        Arrays.fill(dp, BigInteger.valueOf(1));
        //至于这里为什么是1,2,3?是因为这里是剪掉的部分的最大值,如5剪成2*3,那么3的部分最大值是3,因为3在不剪的情况下大于剪之后的,2也同理。
        dp[2] = BigInteger.valueOf(2);
        dp[3] = BigInteger.valueOf(3);
        for (int i = 4; i<=n;i++){
            //j<=i/2是因为1*3和3*1是一样的,没必要计算在内,只要计算到1*3和2*2就好了【减少计算量】
            for (int j = 1;j<= i/2;j++){
                //max(j*(i-j),j*dp[i-j]是由于减去第一段长度为j的绳子后,可以继续剪也可以不剪
                //如果不剪就是j*(i-j),如果继续剪那么dp[i-j]就是这部分的剪得效果的最大值
                //max(dp[i],Math.max(j*(i-j),j*dp[i-j]))是当j不同时,求出最大的dp[i]
                dp[i] = dp[i].max(BigInteger.valueOf(j * (i - j))).max(dp[i - j].multiply(BigInteger.valueOf(j)));

            }
        }
        return  dp[n].mod(BigInteger.valueOf(1000000007)).intValue();
    }
}

 

posted @ 2022-11-16 19:14  忧愁的chafry  阅读(17)  评论(0编辑  收藏  举报