《剑指offer》中,第14题,剪绳子是一个引子,引出了动态规划和贪婪算法。
“动态规划”一词显得十分高深,当时上学的时候就不是很理解。现在多多少少比当时有所进步,看了书之后,感觉理解比之前要深了,现在把这些理解分享出来。
首先不要怕,别管它为什么叫“动态规划”,你也不用想怎么才能动态,如何才能规划,这只是个名字而已,叫他什么都行。
书中讲了动态规划的四个特点,总结如下:
1:目的是求最优解
2:最优解依赖于子问题的最优解
3:大问题可以分解成小问题,这些小问题之间有重叠
4:从下往上求解
现在来一一解释一下,第一条,目的是求最优解,这种情况下,不只是让求解,而是让求问题的最优解。
第二条概括来说就是“千里之行始于足下”和“勿以善小而不为”,我们既然要实现大问题的最优解,那么我们一定要把眼前的事做好,每一步都走好,最终到达终点时我们才是以最优地方法到达。
第三条,前半句不说了,后半句是说,子问题之间会有重叠,因为问题分解小了,有些子问题是重复的,这样就不要重复计算了,所以一般动态规划都会有一个数组来保存子问题的最优解。
第四条,从下往上求解,如果理解了前三条,第四条就好理解了,因为最优解是依赖子问题的最优解的,所以一定要先从简单的问题开始解决。小问题解决了,大问题就迎刃而解了。
先看题,边看边说。
给你一根长度为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
按照上面的四个特点来跟题目对下,
第一条,最优解,题目也是要求乘积最大
第二条,大问题最优解取决于子问题,我们先切一刀,变成两根绳子了,那么乘积最大值是这两段各自的最大值之积,那么这两段绳子的最优解又依赖它们各自的子绳段。
第三条,各个小问题之间存在重叠,是不是这样的呢?可以想到,当我们的绳子切得足够段,一定会出现相同的情况,这些就是重叠的部分。
第四条,就是我们要看到的,我们必须从绳子长度为1 2 3 4开始推,因为长度为 5 6 7 8 9的最优解是依赖他们的。
我们首先要解决几个特殊解,长度为0,答案是0,长度为1,答案也是0,长度为2,答案为1,长度为3,答案为2。
先看代码再答疑。
class Solution {
public:
int cuttingRope(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
int max_table[n+1] ;
max_table[0] = 0;
max_table[1] = 1;
max_table[2] = 2;
max_table[3] = 3;
for(int i = 4 ; i <= n ; i++){
int max = 0;
for(int j = 1; j <= i/2; j++){
if(max_table[j]*max_table[i-j]>max)
max = max_table[j]*max_table[i-j];
max_table[i] = max;
}
}
return max_table[n];
}
};
首先是几个特殊解。
接下来要建一个表格,用来保存子问题的最优解。
肯定有不少同学认为,前几个最优解就是我们开始给的特殊解,其实并非如此。
对于这点的解释,leetocode上的题解给了很多数学推导,但并没从实际问题上做出解释,数学是实际问题的抽象,如果数学推导的通,那么也应该可以给出合理的解释。
table: i 0 1 2 3 4
value 0 1 2 3 4
这里的 i 该如何理解,才能更好地解释这个问题呢,如果 i 代表绳子的长度,那么i = 4时,我们可以很好的理解,那么 i = 3的时候就不好理解了,长度为3,最优解应该是2呀,对不对。
其实,我们可以这样想,每一位上的数,并非说单单指长度为 i 时的结果,而是要表示当前绳子长度为 i 时,与另一端长度为 j 的绳子做乘积时,所能提供出来的最大值。当绳子本身已经是子绳段时,可以不切了(与特殊解不一致是因为绳子必须得切),把自己本身的长度直接给对方去乘,也就说, 其实 i 所对应的value 是
max(i, i 的子问题最优解)
以 3和5 为例
tabel[3] = max(3,table[1]*table[2]) = 3
table[5] = max(5,table[2]*table[3]) = 6
只不过,我们会发现,当 i 的数字越大时,子问题的乘积会远远大于本身。如果理解了这个表格的真正含义,那么这个问题就不在话下了。