算法思想(动态规划)

一、穷举法思想

在有限条件里,将所有符合条件的元素一一带入问题进行检验,从而得到检验通过的精确解。

举例一、

输出一个字符串里最长回文子串长度

思路:根据定义,根据条件(输入的字符串子串)带入问题一一检验,并且得到检验通过(最长的回文子串)的精确解。

拆解思路分析:

1、假设输入字符串:abcdcbc

2、检验所有子串

 2.1、由字符a作为子串首字母遍历出所有的子串,并对每一个子串进行校验,判断是否为回文字符串。由此可以判断第一轮循环时间复杂度是O(n*m).注:m设为回文子串的检验时间复杂度。 如下图2-1

                                                                          图2-1

 2.2、由字符b作为子串首字母遍历出所有的子串,并对每一个子串进行校验,判断是否为回文字符串。由此可以判断第二轮轮时间复杂度是O(n*m).注:m设为回文子串的检验时间复杂度。 如下图2-2

                                                                           图2-2

2.3、知道以最后一个字符为首字母遍历出所有的子串,并对每一个子串进行校验,判断是否为回文字符串,由此可见总体上需要有n次首字母为首遍历所有的子串,故总体的时间复杂度是O(n*n*m)

3、校验是否是回文字符串,验证方式如图3-1

每验证一个字符串,需要左右指针分别标识字符串的最左端和字符串的最右端的字符,当左右指针表示的字符相等,则左右指针相向运动一格,当左右指针标识的字符串不想等时则其不是回文字符串,当左指针位置 > 右指针位置则该字符串事回文字符串,所以可以认为验证回文字符串的时间复杂度为O(n/2) = O(n)。故m = O(n)。

可以看到穷举法做这道题整个的时间复杂度是O(n^2 * m) = O(n^3)。借助的额外空间也就是左右指针,故空间复杂度O(1)。

穷举法引发的思考:

穷举法虽然简单简单易懂,但是时间复杂度太高了,所以有什么方式可以将时间复杂度给降低?当然要思考这个问题,就需要去看时间都花在哪了 

第一:遍历所有子串,花费时间复杂度:O(n ^ 2)。只有拿到子串才能知道是不是回文字符串,所以这一步看似少不了

第二:校验子串是否为回文字符串,花费时间复杂度:O(n)。由于校验子串bcdcb和子串cdc都花费了O(n)的时间复杂度,但是我们可以先验证cdc是回文子串,设为串A,那么字符串bcdcb则为bAb,由此可见只需要去校验首字符和尾字符是否相等就能知道bcdcb是否为回文字符串。不需要重新校验cdc串是否为回文字符串,故可以采取空间换时间的概念,先处理长度短的子串,再处理长度加1的子串,依次处理越来越长的子串。将每次处理结果记录下来,那么校验长串是否为回文子串,只需要去校验头、尾字符是否相等和短串处理的结果即可知道长串是否为回文字符串。所以时间可以缩减到O(1),当然代价就是空间复杂度提升。这也引出来了另一个算法的思想,就是动态规划。

二、动态规划法

当一个问题呈现某种规律求最优解,一个问题可以被拆解成多层子问题,并且上层问题可以依托所有下层问题的最优解从而得到上层最优解,如此下去,直到得到顶层也就是原问题的最优解。

还以输出一个字符串里最长回文子串长度为例子,来说明动态规划法的定义。 如图3所示

                                                                           图3 

红色标记:第四层abcd串由第二层的bc串是否是回文字符串和a,d字符是否相等来判断abcd是否是回文字符串,所以bc串不是回文字符串,那么abcd肯定不是回文字符串。

蓝色标记:第三层cdc是否是回文字符串,依靠第一层字符串d和首、尾字符是否相等判断是否为回文字符串,是则长度为1+2。第五层字符串bcdcb是否是回文字符串,依靠第三层字符串cdc和首、尾字符是否相等判断是否为回文字符串,如果是,则长度为cdc长度加2。第七层abcdcbc是否是回文字符串,依靠第五层字符串bcdcb和首、尾字符是否相等判断是否为回文字符串。

重要因素

"规律",当有符合一定规律的事物,一定可以通过表达式方程表示出来。所以列出表达式方程是使用动态规划最重要的一环。

使用动态规划解例题一

第一步:列出表达式方程。注,通过前面的穷举法可以分析出来,判断子串是否是回文字符串这一步是优化点,而优化的思想就是动态规范。所以isHw返回的就是是否是回文字符串,i表示子串的头,j表示子串的尾,故方程如图4所示

                                                                         图4

第二步:实现方程(不展示代码,通过图来描述整个过程,重在理解,以下图中"T"表示true,"F"标识FALSE),s表示输入字符串假设为“abcdcbc”

动态规划还有一个关键点就是 空间换时间。图中方程的值isHW(i, j)是具有两个变量i,j(从第i个位置到第j个位置的子串)。所以该动态规划可以定义成二维的动态规划,可以使用二维数组来存储isHW(i, j)的值

所以代码实现先初始化一个二维数组

                                                                图5-1

 根据方程进行数组的填充

4.1、首先填充i == j的情况,填充后如下图。

                                                               图5-2

最长的回文子串长度是1

 4.2、再填充i == j -1的情况,此时代码中需要判定s[j] 是否等于s[i]。也就是判断图3中第二层每个字符串的左右两个字符是否相等,填充完毕可以得到下图

                                                                图5-3

最长的回文子串长度是1

 4.3、此后接着填充 填充i == j - 2,i == j - 3的情况,此时会走到方程是的isHW(i + 1, j - 1) &&

s[i] == s[j] 。

当i == j - 2时 , 此时 i + 1 == j - 1。 故isHW(i + 1, j - 1)为4.1步所得到的值,都为ture,所以需要判断s[i] 是否等于s[j]

当i == j - 3时, 此时 (j - 1) - (i + 1) == 1。故isHW(i + 1,j - 1)为4.2步所得到的值,都为false,所以isHW(i, j)都为false

如下图所示

                                                                图5-4

 最长的回文子串长度是3

 4.4 、通过4.3的处理方式扫尾处理剩下的所有子串,可以得到下图

                                                                图5-5

 最长的回文子串长度是5

由此通过动态规划求解出来的例题一最长回文子串长度是5

复杂度 

时间复杂度O(n ^ 2)空间复杂度O(n ^ 2)

动态规划写法的优化

通过例题1可以看到使用动态规划的思想去代替穷举法的思想,确实时间复杂度降低了,由O(n^3) 降到了O(n^2),但是空间复杂度的开销由O(1)升到了O(n^2),看起来并不划算。

那么如何去优化呢?

动态规划方程式通过代码实现后,可以将一个问题通过平面图抽象出来代码的运行过程,那么就可以通过平面图去找到优化点,从而使问题更加直观。

我们知道动态规划一个问题可以被拆解成多层子问题,如何去减少子问题解的存储是动态规划优化的重要一环。

可以想一下能否得到最底层一个子问题的,就求出所有与该层相关的上层问题的解呢?

有,不妨将思路转变一下,如图6

                                                                       图6
可以看到优化后可以由左下角的箭头一直遍历到右上角的箭头,可以设三个变量y表示isHW(i,  j),z表当前回文子串长度,max表示回文子串的最大长度,s表示字符串。注:下面所说的元素指代途中二维数组中下标为:(i,j)标识的空间。

实力字符串:"abcdcbc"

分析步骤(由左下角的箭头一个个遍历到右上角的箭头,每个箭头的遍历方向跟随箭头方向)

第一个箭头:涉及到一个元素,那么 y = T,  z = 1。max = 1

第二个箭头:涉及到一个元素,那么 y = F,  z = 0。max = 1

第三个箭头:涉及到两个元素,那么处理第一个元素:x = T, z = 1,处理第二个元素时:x = x && s[0] == s[2] ,x = F, z = 1。max = 1

第四个箭头:涉及到两个元素,处理第一个元素:x = F,z = 0,后续元素不需要处理,都是F。max = 1

第五个箭头:涉及到三个元素,处理第一个元素:x = T,z = 1,处理第二个元素时:x = x && s[1] == s[3],x = F,z = 1。后续元素不需要处理,都是F。max = 1

第六个箭头:涉及到三个元素,处理第一个元素:x = F,z = 0,后续元素不需要处理,都是F。max = 1

第七个箭头:涉及到四个元素,处理第一个元素:x = T,z = 1,处理第二个元素时:x =    x && s[2] == s[4],x = T,z =  z + 2 = 3,max = max > z ? max : z, max  = 3 。处理第三个元素时:  x =   x && s[1] == s[5],x = T,z =  z + 2 = 5,max = max > z ? max : z, max  = 5 。处理第四个元素时:  x =   x && s[0] == s[6],x = F,z =  5,max = max > z ? max : z, max = 5 。

........(依赖上面处理箭头的思路处理剩下的箭头)

最后可得Max = 5。

优化后,辅助空间上的成本就是x , z, max。空间复杂度:O(1),时间复杂度O(n)。

练习两道经典动态规划问题

1、爬楼梯问题

一个人爬楼梯,一次只能爬一阶或两阶,当总共有n阶楼梯时,有几种爬法。

1、分析题目,想想轮询法怎么实现:轮询法是从第一层楼梯开始走走一步或者两步的排列组合都记录了(可有可无)

2、轮询法的优化点:运动到某一点的所有走法记录下来,从而以该点为起点去算更高层的楼梯,从而不需要从父计算低点的走法。

3、列出方程表达式:该题与例题一的区别是,例题一是通过两个点i,j确定子串的,所以拥有两个变量决定子问题。而该题固定是从第一层到第n层,通过一个点n确定子串,所以拥有一个变量,故表达式是依据这一个变量进行展开的

4、解题思路:由于该方程式仅有一个变量n,所以通过一个一位数组进行处理即可

假设n = 10,则需要使用一维数组作为一个辅助空间

 

5、优化:根据抽象出来的一维数组和方程式,可以知道想求到第n层的爬法,只需要知道到第n-1层的爬法,和到第n - 2层的爬法相加即可。故只需要三个辅助空间j , k , l即可,j代表n- 2层的爬法,k代表 n - 1层的爬法,l代表n层的爬法。

可以理解为:

当计算第i层时,2 < i < n - 1。那么 j = k, k = l , l = k + l;

 当计算第i + 1层时,3 < i + 1 < n。那么 j = k, k = l , l = k + l;

故最后可求得爬法为l。

最终时间复杂度为O(n),空间复杂度为O(1)。

2、背包问题(讲解完全背包)

有N种物品和一个容量为V的背包,每种物品都有无限件可用,第i件物品消耗的容量为Ci,价值为Wi,求解放入哪些物品可以使得背包中总价值最大。

1、分析题目,想想轮询法怎么实现:选择一个物品放进背包,有放和不放两种状态,故通过轮询法将背包填入n个物品的时间复杂度是O(2^n)。

2、可优化点,当选择放置一个物品i时,其可再放入的最大容量则为V-Ci,就相当于需要求解一个最大容量为V-Ci的背包放置的最大价值。所以V-Ci 容量的背包可以看成一个子问题。

3、列出表达式方程:可以看到背包背的最大价值取决于背包的容量V,和放置物品的数量,当数量越多背包容量越少,所以背包的容量和物品的重量是变量。列出表达式方程

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值