一、前言

《剑指Offer》中题14

二、题目

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

三、思路

第一种解法:递归自顶向下解法

当第一次看到这个问题的时候第一反应的什么呢?有点蒙,再细细想想这个问题。将一根绳子分为m段其乘积最大,m段必定有两部分组成定为m1,m2,要想m段乘积最大,就需要保证m1段中的分割乘积是最大,同理m2也需一样。先看下面图,n第一次分割之后如下:

备注,第一行和下面组合不是两个值直接相乘,而是拆分之后最大的值相乘,例如 m1 长度为 6, m2 长度 n-6,m1分割最大乘积是9(3*3)

最大的结果肯定为其中一个组合,即:f(n) = max(f(i) X f(n-i));同理 f(i),f(n-i)又可以往下切割,直到最小单位位置,最小切割单位为 f(1) = 1,f(2) = 2, f(3) = 3。

分析完成脑海立马想到的是递归解法,递归简单易懂。

第二种解法:递推自下而上解法

递归很好理解,但递归重复了大量计算,随着n值增大,效率呈指数下降。递归是自顶向下,那有没有自下而上的解法呢?

回顾一下斐波那契数列第二种解法,它是每次计算f(k),f(k+1)的值,通过f(k),f(k+1)可以求出f(k+2),通过f(k+1),f(k+2) 可以求出f(k+2),一直到f(n)。

同理我们要求f(k) = max(f(i) X f(k-i)),我们需要提前记录f(i),f(k-i)最大的值,循环遍历计算之后就能求出f(k)最大值,同理以此往下计算到f(n)为止就可以了

第三种解法:贪心解法(更像是数学推导的结果)

当 n >= 5 时,可以证明:2(n-2) > n && 3(n-3) > n。也就是说当绳子剩下的长度大于或者等于5的时候,我们可以将绳子剪成长度为 3 或 2。另外,当 n >= 5时,3(n-3) >= 2(n-2),因此我们应该将可能多的剪成长度为3绳子段。

需要注意的是 第一种和第二种解法属动态规划,第三属贪心算法。后续会详细讲解。

四、注意事项

多领悟里面的思想。

五、编码实现

#include <iostream>
#include <cmath>// ====================动态规划====================// 解法一: 递归自顶向下解法
int maxProductSubset(int length)
{if (length <= 0){return 0;}int maxProductArr[] = { 0, 1, 2, 3, 4 };if (length < sizeof(maxProductArr) / sizeof(maxProductArr[0])){return maxProductArr[length];}int maxProduct = 0;for (int i = 1; i <= length / 2; ++i){int maxProductT = maxProductSubset(i) * maxProductSubset(length - i);maxProduct = maxProduct > maxProductT ? maxProduct : maxProductT;}return maxProduct;
}int maxProductAfterCutting_solution1(int length)
{if (length <=0){return 0;}int maxProductArr[] = { 0, 0, 1, 2, 4 };if (length < sizeof(maxProductArr) / sizeof(maxProductArr[0])){return maxProductArr[length];}int maxNum = 0;for (int i = 1; i <= length / 2; ++i){int product = maxProductSubset(i) * maxProductSubset(length - i);maxNum = maxNum > product ? maxNum : product;}return maxNum;
}// 解法二: 递推自下而上解法
int maxProductAfterCutting_solution2(int length)
{if(length < 2)return 0;if(length == 2)return 1;if(length == 3)return 2;int* products = new int[length + 1];products[0] = 0;products[1] = 1;products[2] = 2;products[3] = 3;int max = 0;for(int i = 4; i <= length; ++i){max = 0;for(int j = 1; j <= i / 2; ++j){int product = products[j] * products[i - j];if(max < product)max = product;}products[i] = max;}max = products[length];delete[] products;return max;
}// ====================贪婪算法====================
// 解法三: 贪心算法
int maxProductAfterCutting_solution3(int length)
{if(length < 2)return 0;if(length == 2)return 1;if(length == 3)return 2;// 尽可能多地减去长度为3的绳子段int timesOf3 = length / 3;// 当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段。// 此时更好的方法是把绳子剪成长度为2的两段,因为2*2 > 3*1。if(length - timesOf3 * 3 == 1)timesOf3 -= 1;int timesOf2 = (length - timesOf3 * 3) / 2;return (int) (pow(3, timesOf3)) * (int) (pow(2, timesOf2));
}// ====================测试代码====================
void test(const char* testName, int length, int expected)
{if(length < 50){// 递归效率太低,length较大时执行时间很长int result1 = maxProductAfterCutting_solution1(length);if(result1 == expected)std::cout << "Solution1 for " << testName << " passed." << std::endl;elsestd::cout << "Solution1 for " << testName << " FAILED." << std::endl;}int result2 = maxProductAfterCutting_solution2(length);if(result2 == expected)std::cout << "Solution2 for " << testName << " passed." << std::endl;elsestd::cout << "Solution2 for " << testName << " FAILED." << std::endl;int result3 = maxProductAfterCutting_solution3(length);if(result3 == expected)std::cout << "Solution3 for " << testName << " passed." << std::endl;elsestd::cout << "Solution3 for " << testName << " FAILED." << std::endl;
}void test1()
{int length = 1;int expected = 0;test("test1", length, expected);
}void test2()
{int length = 2;int expected = 1;test("test2", length, expected);
}void test3()
{int length = 3;int expected = 2;test("test3", length, expected);
}void test4()
{int length = 4;int expected = 4;test("test4", length, expected);
}void test5()
{int length = 5;int expected = 6;test("test5", length, expected);
}void test6()
{int length = 6;int expected = 9;test("test6", length, expected);
}void test7()
{int length = 7;int expected = 12;test("test7", length, expected);
}void test8()
{int length = 8;int expected = 18;test("test8", length, expected);
}void test9()
{int length = 9;int expected = 27;test("test9", length, expected);
}void test10()
{int length = 10;int expected = 36;test("test10", length, expected);
}void test11()
{int length = 50;int expected = 86093442;test("test11", length, expected);
}int main(int agrc, char* argv[])
{test1();test2();test3();test4();test5();test6();test7();test8();test9();test10();test11();return 0;
}

执行结果:

剪绳子(动态规划、贪心算法)相关推荐

  1. 152. Leetcode 剑指 Offer 14- II. 剪绳子 II (贪心算法-基础题目)

    class Solution:def cuttingRope(self, n: int) -> int:if n < 4:return n - 1res = 1while n > 4 ...

  2. 剑指Offer:剪绳子(动态规划、贪婪算法)

    问题描述 给你一根长度为n的绳子,请把绳子剪成m段(m.n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],-,k[m].请问k[0]xk[1]x-xk[m]可能的最大乘 ...

  3. 88. Leetcode 剑指 Offer 14- I. 剪绳子 (动态规划-基础题)

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m.n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] .请问 k[0]*k[1]*... ...

  4. 【LeetCode】LeetCode之跳跃游戏——动态规划+贪心算法

    [LeetCode]LeetCode之打家劫舍[暴力递归.动态规划.动态规划之优化空间的具体分析与实现] https://blog.csdn.net/Kevinnsm/article/details/ ...

  5. 【算法】【动态规划篇】第5节:剪绳子问题

    本期任务:介绍算法中关于动态规划思想的几个经典问题 [算法][动态规划篇]第1节:0-1背包问题 [算法][动态规划篇]第2节:数字矩阵问题 [算法][动态规划篇]第3节:数字三角形问题 [算法][动 ...

  6. 贪心算法,递归算法,动态规划算法比较与总结

    一般实际生活中我们遇到的算法分为四类: 一>判定性问题        二>最优化问题        三>构造性问题        四>计算性问题 而今天所要总结的算法就是着重解 ...

  7. 【算法导论】贪心算法,递归算法,动态规划算法总结

    一般实际生活中我们遇到的算法分为四类: 一>判定性问题 二>最优化问题 三>构造性问题 四>计算性问题 而今天所要总结的算法就是着重解决 最优化问题 <算法之道>对 ...

  8. 递归、迭代、分治、回溯、动态规划、贪心算法

    今天就简单来谈谈这几者之间的关联和区别 递归 一句话,我认为递归的本质就是将原问题拆分成具有相同性质的子问题. 递归的特点: 1.子问题拆分方程式,比如:f(n) = f(n-1) * n 2.终止条 ...

  9. 程序员都会的五大算法之三(贪心算法),恶补恶补恶补!!!

    前言 点击查看算法介绍 五大算法 分治算法 动态规划 贪心算法 回溯算法 分支限界算法 WX搜素"Java长征记"对这些算法也有详细介绍. 贪心算法 一.算法概述 贪心算法也叫贪婪 ...

  10. 贪心算法-活动安排问题

    贪心算法总是作出在当前看来最好的选择.也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择.当然,希望贪心算法得到的最终结果也是整体最优的.虽然贪心算法不能对所有问题都得到 ...

最新文章

  1. PHP开发之递归算法的三种实现方法
  2. centos6_64位系统安装部署puppet(master、agent)
  3. NodeJS http服务端获取POST请求数据
  4. 什么叫单模光纤_什么叫单模光纤_单模光纤的特点是什么
  5. centos7 hive mysql_CentOS7搭建Hive1.2.2+mysql5.7
  6. java记事本课程设计,java记事本课程设计
  7. python如何读取文本_python 如何读取windows-1252格式文本?
  8. 翻译:探索GLSL-用几何着色器(着色器库)实现法线可视化
  9. 图像处理九:拟合曲线
  10. VisualSVN Server提交整个工程项目
  11. Appium Server
  12. JAVA实现饭店点菜系统详解
  13. 外卖扫码点餐独立全开源小程序源码+VUE前端
  14. Windows 10操作系统常用快捷键介绍
  15. MB/s与Mbit/s的区别!!!
  16. Unreal Engine 虚幻引擎,性能优化
  17. 【AWS云从业者基础知识笔记】——模块8:定价和支持
  18. 测试人员为什么也要学习Linux操作系统
  19. Xmind基础教程-保存到印象笔记
  20. 我的一百个2019(三):2019,我赚钱了!

热门文章

  1. 困难负样本挖掘方法——OHEM
  2. MQ报错2009/2085解决方法
  3. 【每日早报】2019/07/22
  4. mysql 强制使用索引 FORCE INDEX(idx_name)
  5. C++Primer5th 第十九章 特殊工具与技术
  6. 身份证号码、手机号码格式校验
  7. DevOps自动化之Jenkins
  8. 解决电视端使用SMB播放电脑资源需要输入密码的问题
  9. Web基础——Html基础
  10. 如何使用word 2016公式编辑器更快的编写LateX格式公式(专业)?