【剑指Offer】【动态规划&贪婪算法】14_剪绳子 (说实话,还是不太懂)
题目:给你一根长度为n的绳子,请把绳子剪成m段 (m和n都是整数,n>1并且m>1)每段绳子的长度记为k[0],k[1],...,k[m].请问k[0]*k[1]*...*k[m]可能的最大乘积是多少?例如,当绳子的长度为8时,我们把它剪成长度分别为2,3,3的三段,此时得到的最大乘积是18.
动态规划
总是从解决最小问题开始,并把已经解决的子问题的最优解存储下来(一维或二维数组),并把子问题的最优解组合起来逐步解决大的问题。
动态规划的特点:
- 问题的目标是,求一个问题的最优解:问题的目标是求剪出各段绳子长度的乘积最大值
- 整体问题的最优解是以来各个子问题的最优解:把第一刀剪在i的位置,绳子分为i和n-i;用同样优化的方法把i和n-i分别剪成若干段......依此类推
- 把大问题分解为若干个小问题,小问题之间还有互相重叠的更小子问题:假设绳子起初长度为10,第一刀分为4和6;第二刀分4的,得两段2;第三刀分6的,得2和4......那么f(2)是f(4)和f(6)的公共子问题
- 从上到下分析问题,从下往上求解问题:避免重复求解子问题
贪婪算法
每一步都可以做出一个贪婪的选择,基于这个选择,可以得到最优解。
比如长度为10的绳子,如果绳子长度大于3,则剪长度为3的段,若剩余绳子长度仍大于3,则继续剪3的段......直到剩余的绳子长度小于3
//动态规划
f(n) = max(f(i) * f(n-i)) , 0<i<n
为了避免重复子问题,先得到f(2),f(3)再得到f(4),f(5)......
当绳子长度为2的时候,只能剪两段1 ===> f(2) = 1;
当绳子长度为3的时候,能剪一段2和一段1 或者 三段1 ===> 1*2 > 1*1*1 ===> f(3) = 2
class Solution { public: int maxProductAfterCutting_solution(int length) { if(length < 2) { return 0; } if(length == 2) { return 1; } if(length == 3) { return 2; } //子问题的最优解存储在数组product中 int *product = new int[length + 1]; product[0] = 0; product[1] = 1; product[2] = 2; product[3] = 3; //product[i]的第i个元素表示把长度为i的绳子剪成若干段后各段长度乘积的最大值,f(i) int max = 0; //求i之前对于每个j,f(j)都已经求解出来了,并且结果保存在product[j]里 for(int i = 4; i <= length; i++) { //求出所有可能的f(j)*f(i-j)并比较它们的最大值 max = 0; for(int j = 1; j = i / 2; j++) { int tmp = product[j] * product[i - j]; if(max < tmp) { msx = tmp; } product[i] = max; } } max = product[length]; delete[] product; return max; } };
//贪婪算法
当n>=5时,尽可能多地剪长度为3的绳子,当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子
class Solution { public: int maxProductAfterCutting_solution(int length) { if(length < 2) { return 0; } if(length == 2) { return 1; } if(length == 3) { return 2; } //尽可能多地取剪长度为3的绳子段 int timesOf3 = length / 3; //当绳子最后剩下的长度为4的时候:把绳子剪成长度为2的两段,因为2*2 > 1*3 if((length - timesOf3 * 3) == 1) { timesOf3 -=1; } int timesOf2 = (length - timesOf3 * 3) / 2; //返回 x 的 y 次幂。 return ((int)(pow(3,timesOf3)) * (int)(pow(2,timesOf2))); } };