【面试题】剪绳子问题(动态规格+贪心算法,考虑大数问题)

题目一

在这里插入图片描述

解法一:动态规划

class Solution {
    public int cuttingRope(int n) {
        if(n <= 1) return 0; // 题目要求至少需要切一次,长度小于2没法切
        if(n == 2) return 1; // 1*1=2
        if(n == 3) return 2; // 1*2=2
        int[] dp = new int[n+1]; // dp数组中第 i-1个元素存放把长度为 i的绳子剪成若干段之后各段长度乘积的最大值,即各个子问题的最优解。
        dp[0] = 0; // 长度为1的绳子对应结果
        dp[1] = 1; // 长度为2的绳子对应结果
        dp[2] = 2; // 长度为3的绳子对应结果
        int max_num;
        for(int i=3; i<=n; i++) {
            max_num = 0; // 保留内循环的最大值
            for(int j=1; j<=i/2; j++) { // 前面切j,后面剩 i-j, 等价于前面切 i-j,后面剩 j。因此 j只需遍历到 i/2这里就可以
                // 将当前长度为 i的绳子从 j处剪一下,得到 j和 i-j的两部分,比较继续剪 i-j部分(j*dp[i-j])和不继续剪(j*(i-j))的乘积较大者。
                int tmp = Math.max(j*dp[i-j], j*(i-j));
                if(max_num < tmp) max_num = tmp; // 如果内循环本轮 j对应的乘积最大值大于之前的 j对应的乘积最大值,则更新             
            }
            dp[i] = max_num; // 内循环结束,将搜索到的最大值存放到 dp[i]中
        }
        return dp[n];
    }
}

解法二:DP优化

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n-1;
        int[] dp = new int[n+1];
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for(int i=4; i<=n; i++) {
            dp[i] = (2*dp[i-2])>(3*dp[i-3]) ? 2*dp[i-2] : 3*dp[i-3];
        }
        return dp[n];
    }
}

解法三:贪心算法

class Solution {
    public int cuttingRope(int n) {
        if(n <=3) return n-1;
        int res = 1;
        while(n >4){
            res = res*3;
            n -= 3;
        }
        return res*n;
    }
}

解法四:数学法

详细题解参考这篇文章

class Solution {
    public int cuttingRope(int n) {
        /*
        最优切分子段长度为 3
        当 n≤3 时,应不切分,但由于题目要求至少剪一次,因此只能剪出一段最小的长度为 1的绳子,即返回 n-1。
        */
        if(n <= 3) return n-1;
        // 求 n除以 3的整数部分 a(可以得到多少个长度为3的子段)和余数部分 b(留下的最后一段绳子长度只可能为0,1,2三种情况)
        int a = n/3;
        int b = n%3;
        if(b == 0) return (int)Math.pow(3, a); // 可以整除3,直接求3的a次幂
        else if(b == 1) return (int)Math.pow(3, a-1)*4; // 若最后一段绳子长度 b=1, 则应把一份 3+1替换为 2+2,因为 2×2>3×1。返回3^(a-1)*2*2
        else return (int)Math.pow(3, a)*2; // 若最后一段绳子长度 b=2, 则应保留,不再拆分为 1+1。返回3^(a)*2
    }
}

在这里插入图片描述

题目二(考虑大数问题)

在这里插入图片描述
此题和上一题的唯一不同在于本题目涉及 大数越界情况下的求余问题

解法一:数学法

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n-1;
        int a = n/3, b = n%3, p = 1000000007;
        long x = 3; // 至少要保证 x可以正确存储 1000000007^2
        if(b == 0) return (int)remainder(x, a, p); // 可以整除3,直接求3的a次幂
        if(b == 1) return (int)(remainder(x, a-1, p)*4 %p); // 若最后一段绳子长度 b=1, 则应把一份 3+1替换为 2+2
        return (int)(remainder(3, a, p)*2 %p); // 若最后一段绳子长度 b=2, 则应保留,不再拆分为 1+1。返回3^(a)*2
    }

    // 快速幂求余法 求解 (x^a) % p 
    public long remainder(long x, int a, int p) {
        long res = 1;
        while(a > 0) { // 快速求幂法思路,同时每次取余
            if(a % 2 == 1) // 判断为奇数次幂时,将多出的一项乘入 res中
                res = (res*x) % p;
            x = (x*x) % p;
            a /= 2; // 对指数不断二分
        }
        return res;
    }
}

解法二:贪心算法

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n-1; 
        if(n == 4) return 4; // 4最优是拆成 2+2,但 2*2结果等同4本身
        int mod = (int)1e9+7;
        long res = 1;
        while(n >= 5) { // 循环求余法,只考虑4以上的长度是因为,当n=4时,最大乘积2x2和自身是等同的
            res *= 3;
            res %= mod;
            n -= 3; // 每次都优先剪出长度为3的子段
        }
        // 尽可能多地剪出长度为3的子段,直到绳子长度<5,则不需要再切分了,直接保留该段,故乘上最后剩余的n
        return (int)(res * n % mod); 
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值