一、前言

《剑指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. python 特征选择 绘图 + mine
  2. apt-get install php 指定版本号,如何使用apt-get升级单个软件包?
  3. Linux启动redis提示 /var/run/redis_6379.pid exists, process is already running or crashed
  4. 推荐一个MDI模式的远程桌面管理程序
  5. qt翻译--QDragMoveEvent拖放类
  6. yb3防爆电机型号含义_YBK3防爆电机介绍
  7. Builder生成器模式
  8. 如何进行动态的SQL环境搭建?
  9. Java之spring新手教程(包教包会)
  10. 基于java的客户关系管理系统的设计与实现(论文+毕业设计)(完整源码可演示)
  11. 计算机word文本段落位置互换,word中调换位置 用word怎么使两个段落互换位置
  12. 认识IBinder和Binder
  13. py.切片(slice)
  14. 基于强化学习的图像配准 - Image Registration: Reinforcement Learning Approaches
  15. 使用Nginx负载均衡及动静分离
  16. 新能源汽车里程宣传有夸大之嫌
  17. Kotlin开发第四天,探究Fragment
  18. jQuery dialog实现登陆,增删改操作
  19. AI算法让图片动起来,特朗普和蒙娜丽莎深情合唱《Unravel》
  20. Python数据可视化的例子——画图总结

热门文章

  1. 12期 11月期刊自荐
  2. swfobject加载flash
  3. 零基础学python 视频_全网最全Python视频教程真正零基础学习Python视频教程 490集...
  4. Java -Xms -Xmx -Xss -XX:MaxNewSize -XX:MaxPermSize含义记录
  5. 开放系统理论(3)生物的特征——热力学第二定律,和熵(1吐槽)
  6. 因特理臻深度学习系统培训教程
  7. 开源GIS系统(转载)
  8. macbook pro适合python编程么_m1的MacBook pro适合python开发吗?
  9. 107-周跳探测之MW
  10. 基于c++的opencv学习记录