什么是动态规划
把原问题分解成若干个相对简单的子问题,然后逐步解决子问题从而解决复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。
基本思想
若要解出一个相对复杂的问题,我们需要解出其子问题,再合并子问题从而得到原问题的解。
问题特征
最优子结构:当原问题最优解包含了其子问题的最优解时,称该问题具有最优子结构。
重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解。
分治与动态规划
共同点:二者都要求原问题具有最优子结构性质,都是将原问题分而治之,分解成若干个规模较小(小到很容易解决的程序)的子问题.然后将子问题的解合并,形成原问题的解.
不同点:分治法将分解后的子问题看成相互独立的,通过用递归来做。
动态规划将分解后的子问题理解为相互间有联系,有重叠部分,需要记忆,通常用迭代来做。
递归和迭代定义
相同点:
递归和迭代都是循环的一种。
不同点:
1、程序结构不同
递归是重复调用函数自身实现循环。
迭代是函数内某段代码实现循环。
其中,迭代与普通循环的区别是:迭代时,循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值。
2、算法结束方式不同
递归循环中,遇到满足终止条件的情况时逐层返回来结束。
迭代则使用计数器结束循环。
当然很多情况都是多种循环混合采用,这要根据具体需求。
3、效率不同
在循环的次数较大的时候,迭代的效率明显高于递归。
动态规划的解决步骤
- 判断题意是否为找出一个问题的最优解
- 从上往下分析问题,大问题可以分解成子问题,子问题还有更小的子问题
- 从下往上分析问题,找出这些问题之间的关联(状态转移方程)
- 讨论底层的边界问题
- 解决问题(通常使用数组进行迭代求出最优解)
例题:找最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
/*使用动态规划 * 步骤1:找到状态转移函数 * 步骤2:找到底界问题两种情况(第一种是最中间是一个字母,第二种是最中间相邻相同) * 步骤3:解决问题 */ class Solution { public String longestPalindrome(String s) { if(s.equals("")) { return ""; } String temp=String.valueOf(s.charAt(0)); //第一种 for(int i=0;i<s.length();i++) { for(int j_min=i-1,j_max=i+1;j_min>=0&&j_max<s.length();j_min--,j_max++) { if(String.valueOf(s.charAt(j_min)).equals(String.valueOf(s.charAt(j_max)))) { if(temp.length()<(j_max-j_min+1)) { temp=s.substring(j_min, j_max+1); } } else break; } } //第二种 for(int i=0,j=1;i<s.length()&&j<s.length();i++,j++) { for(int j_min=i,j_max=j;j_min>=0&&j_max<s.length();j_min--,j_max++) { if(String.valueOf(s.charAt(j_min)).equals(String.valueOf(s.charAt(j_max)))) { if(temp.length()<(j_max-j_min+1)) { temp=s.substring(j_min, j_max+1); } } else break; } } return temp; } }