leetcode刷题---剑指offer---剪绳子---动态规划,贪心算法

给你一根长度为 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。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:

2 <= n <= 58

动态规划写法

这题用动态规划来写的话,dp的意义在于,dp【i】是在i长度下所能有的最大乘积

我们用两层循环来遍历,第一层是当前绳子的长度,第二层是第一刀要剪掉的长度,我们剪了第一刀之后,可以继续剪第二刀,继续剪第二刀的时候,其实就是去获取dp【i-第一刀剪掉的长度】,也可以就此收手。在第二层循环跑完之后我们就可以得到dp【i】的值。

描述的不是很好,下面放上看到的题解

我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来
用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪
剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j]。取两者最大值max(j * (i - j), j * dp[i - j])
第一段长度j可以取的区间为[2,i),对所有j不同的情况取最大值,因此最终dp[i]的转移方程为
dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
最后返回dp[n]即可

作者:edelweisskoko
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/jian-zhi-offer-14-i-jian-sheng-zi-huan-s-xopj/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下面是我根据这种思路写的代码

class Solution {
    public int cuttingRope(int n) {
        if (n == 2) {
            return 1;
        }
        if (n == 3) {
            return 2;
        }
        int[] dp = new int[n + 1];
        dp[2] = 1;
        dp[3] = 2;
        for (int i = 3; i < n + 1; i++) {
            for (int j = 2; j <= i / 2; j++) {
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
}

这里提一下为什么第二层循环是i / 2,这是因为,我们遍历到后半截的时候其实和前面半截是重复的。比如一条长度为10的绳子,我们第一刀切了两厘米和第一刀切了八厘米是等效的,所以我们不需要遍历后半截的内容。
但是需要注意的是在绳子长度为3 的时候这么写是有问题的,因为3 / 2已经为1了,那么就不会执行进循环里面,所以我们要初始化一下dp[3],或者判断一下绳子长度为3这种特殊情况

贪心算法

这题的最优解其实是贪心算法,这题的贪心之处在于尽量将绳子分为长度为3的小段,这样得到的乘积最大
下面放上一个看到的题解

如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
如果 n == 4,返回4
如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段;每次乘法操作后记得取余就行
以上2和3可以合并

作者:edelweisskoko
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/jian-zhi-offer-14-ii-jian-sheng-zi-iihua-r5op/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下面是根据思路写的代码

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

这里的题目是添加了

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

要求的情况,所以答案做了取模,如果没有这个要求就不需要取模,同时res可以用int来定义

之所以while里面的条件是n>4而不是大于三,是因为4是一个特殊情况,在4的时候,我们以3作为切割反而得不到最大的乘积(3x1 < 2x2),所以我们要对4特殊处理,巧合的地方在于4的最大乘积恰好是4,所以当剩余的绳子长度为4的时候,我们可以直接让他作为n和之前得到的res相乘

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值