1. 概念
适用于原问题可以分解为相对简单的子问题方式,子问题非常相似,而且会有重叠部分,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。
类似将递归算法重新写成非递归算法,让后者把那些子问题的答案系统地记录在一个表内。
2. 分治与动态规划
共同点:
- 二者都要求原问题具有最优子结构性质,都是将原问题分而治之,分解成若干个规模较小(小到很容易解决的程序)的子问题.然后将子问题的解合并,形成原问题的解.
不同点:
- 分治法将分解后的子问题看成相互独立的,通过用递归来做。
- 动态规划将分解后的子问题理解为相互间有联系,有重叠部分,需要记忆,通常用迭代来做。
3. 求解问题的特点
(1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
(2)无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
4. 步骤
1、创建一个一维数组或者二维数组,保存每一个子问题的结果,具体创建一维数组还是二维数组看题目而定,基本上如果题目中给出的是一个一维数组进行操作,就可以只创建一个一维数组,如果题目中给出了两个一维数组进行操作或者两种不同类型的变量值,比如背包问题中的不同物体的体积与总体积,找零钱问题中的不同面值零钱与总钱数,这样就需要创建一个二维数组。
注:需要创建二维数组的解法,都可以创建一个一维数组运用滚动数组的方式来解决,即一位数组中的值不停的变化,后面会详细徐叙述
2、设置数组边界值,一维数组就是设置第一个数字,二维数组就是设置第一行跟第一列的值,特别的滚动一维数组是要设置整个数组的值,然后根据后面不同的数据加进来变幻成不同的值。
3、找出状态转换方程,也就是说找到每个状态跟他上一个状态的关系,根据状态转化方程写出代码。
4、返回值,一般是数组的最后一个或者二维数组的最右下角。
5.斐波那契数列
数列变形问题:
跳台阶问题:每次只能跳一个或者两个台阶,跳到n层台阶上有几种方法。
填充长方体问题:将一个2x1的长方体填充到2xN的长方体中,有多少种方法。
public static int solutionFibonacci(int n){
if(n==0){
return 0;
}else if(n == 1){
return 1;
}else{
int result[] = new int[n+1];
result[0] = 0;
result[1] = 1;
for(int i=2;i<=n;i++){
result[i] = result[i-1] + result[i-2];
}
return result[n];
}
6.数组最大连续子序列和
如arr[] = {6,-1,3,-4,-6,9,2,-2,5} 的最大连续子序列和为14。即为:9,2,-2,5
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for(int i =0;i<n;i++){
arr[i] = sc.nextInt();
}
int count = arr[0];
int ret = arr[0];
for(int i=1;i<n;i++){
count = Math.max(count+arr[i],arr[i]);
ret = Math.max(ret,count);
}
System.out.println(ret);
}
}