PD动态规划算法
一阶入门——从斐波那契数列数列看动态规划和备忘录方法与递归的关系
一重循环的动态规划算法实例解析
笔者记
本部分我们通过对斐波那契数列数列的认识来来认识动态规划的内核,在动态规划算法的学习过程中
主要通过阶梯练习来感受动态规划的内核及动态规划的应用和一些注意说明。
package com.hst.pd;
//
/**
* 这是动态规划的引入代码 用来便写 斐波那契数列
* 该数列是动态规划的入门基础
* 原题是这样的
* 给定一个递推式子f(0)=0 f(1)=1 f(n)=f(n-1)+f(n-2)
* 在这种情况下 让你计算f(n)的结果
* @author Administrator
* 该问题的本质是将n变为包含 0 和 1 的结果的值 即分化到最小之后的数 包含多少个 1
*/
import java.util.Scanner;
public class PD1_FibonacciSequence {
static int n=10;
//
static int[] value = new int[n+1];
//
public static void main(String[] args) {
for (int i = 0; i < value.length; i++) {
//将数组进行初始化
value[i]=-1;
}
Scanner input;
//
int value0 = new PD1_FibonacciSequence().fs(n);
for (int i = 0; i < value.length; i++) {
//将数组进行初始化
value[i]=-1;
}
int value1 = new PD1_FibonacciSequence().fs1(n);
for (int i = 0; i < value.length; i++) {
//将数组进行初始化
value[i]=-1;
}
int value2 = new PD1_FibonacciSequence().fs2();
System.out.println("普通递归方法:"+value0+",搜索方法:"+value1+",动态规划方法:"+value2);
}
//递归的方式
public int fs(int n) {
//
if(n==0) {
return 0;
}
if(n==1) {
return 1;
}
return fs(n-1)+fs(n-2);
}
//由于在递归的过程中 有相当多的数的计算是重复的
//因此我们想办法将结果进行记录
//当用到该结果时 我们就可以采用搜索的方式 调用已经计算的结果 从而节省计算时间
public int fs1(int n) {
//这是一种自上而下的计算模式 目的是通过利用已经计算的结果 减少重复计算的量
//相当于是 递归+映射+自上而下的计算模式
if(n==0) {
return 0;
}
if(n==1) {
return 1;
}
if(value[n]==-1){//实质是一种映射取值的方式
value[n]=fs(n-1)+fs(n-2);
}
//如果映射关系有值的话 则 直接返回结果 否则返回计算结果
return value[n];
}
//上述过程是自上而下的模式
//以下介绍一种自下而上的模式 也就是动态规划
//假设我们已知
//0 和1的结果
//那么我们在向上计算的时候 就会方便很多
//实质是 提前存储 + 向上搜索
public int fs2() {
value[0]=0;value[1]=1;
for (int i = 2; i < value.length; i++) {
value[i]=value[i-1]+value[i-2];
}
return value[ value.length-1];
}
//
}
package com.hst.pd;
/**
* 有一个楼梯,总共有n阶台阶,每上一次,可以上一个台阶,也可以上两个台阶,问 爬上这样的一个楼梯,一共有多少不同的方法
* @author Administrator
*
*/
public class PD2_CilmbingStairs {
static int n = 10;
static int[] value = new int[n+1];
public static void main(String[] args) {
for (int i = 0; i < value.length; i++) {
value[i]=-1;
}
int result = new PD2_CilmbingStairs().cilmbingstairs(n);
//
for (int i = 0; i < value.length; i++) {
value[i]=-1;
}
int result2 = new PD2_CilmbingStairs().cilmbingstairs2(n);
//
for (int i = 0; i < value.length; i++) {
value[i]=-1;
}
int result3 = new PD2_CilmbingStairs().cilmbingstairs3();
System.out.println("普通递归方法:"+result+",搜索方法:"+result2+",动态规划方法:"+result3);
}
//递归算法
public int cilmbingstairs(int n) {
if(n==1) {
return 1;
}
if(n==2) {
return 2;
}
return cilmbingstairs(n-1)+cilmbingstairs(n-2);
}
//查询算法---备忘录算法
public int cilmbingstairs2(int n ) {
if(n==1) {
return 1;
}
if(n==2) {
return 2;
}
if(value[n]==-1) {
value[n] = cilmbingstairs2(n-1)+cilmbingstairs2(n-2);
}
return value[n];
}
//动态规划
public int cilmbingstairs3() {
value[0]=1;value[1]=1;value[2]=2;
for (int i = 3; i < value.length; i++) {
value[i]=value[i-1]+value[i-2];
}
return value[value.length-1];
}
}
二阶提升——从二叉树理解动态规划自下向上解决模式
二重循环的动态规划算法实例解析
笔者记
二叉树是自上而下的一个树形结构,是一个天然的递归结构,递归的过程在二叉树中表现为分支的不断
分叉,当问题解决到分叉的叉尾时,该树叉停止分叉,进行另一个子节点的分叉,并将结果返回给上一个父节点
,动态规划就是利用二叉树的这个性质,找到树的末梢,逆向推到上一个父节点,直到根节点,这也体现了
子结构最优即树本身最优的性质。
在这个过程中 我们认识到了一个重要的性质,即动态规划系列肯定有递归结构,其次动态规划可以分解为相似
子问题,相似子问题的最优解是找到最终问题结果的前提和基础。
package com.hst.pd;
/**
* 给定一个正数n,可以将其分割成为多个数字的和,若要让这些数字
* 的乘积最大,求分割的方法,要求至少分割为两个数
* 算法返回最大的乘积值
* 本次是求取最值的动态规划方案
* @author Administrator
*
*/
public class PD3_IntergerBreak {
//
static int n = 10;
static int[] value = new int[n+1];
public static void main(String[] args) {
for (int i = 0; i < value.length; i++) {
value[i]=-1;
}
int result = new PD3_IntergerBreak().IntergerBreak(n);
System.out.println(result);
for (int i = 0; i < value.length; i++) {
value[i]=-1;
}
int result2 = new PD3_IntergerBreak().IntergerBreak2(n);
System.out.println(result2);
for (int i = 0; i < value.length; i++) {
value[i]=-1;
}
int result3 = new PD3_IntergerBreak().IntergerBreak3(n);
System.out.println(result3);
}
//首先最后一步是如何操作的
//最后一步是可以进行数字前的值的选择性分割
//将数字进行分割 至少分割为两部分 可以获得最大乘积
public int IntergerBreak(int n) {//递归暴力破解法
//
if(n==1) {
return 1;
}
int res =-1;
for (int i = 1; i < n; i++) {
res=max3(res,i*(n-i),i*IntergerBreak(n-i));
//按树的理解来进行 可能是本身的分割就是最大的 也可能需要对子树进一步分割才能有最大的
}
//
return res;
}
public int max3(int a,int b,int c) {
//
return Math.max(a,Math.max(b,c));
}
public int IntergerBreak2(int n) {//搜索备忘录法
//
if(n==1) {
return 1;
}
if(value[n]!=-1) {
return value[n];
}
int res =-1;
for (int i = 1; i < n; i++) {
res=max3(res,i*(n-i),i*IntergerBreak(n-i));
//按树的理解来进行 可能是本身的分割就是最大的 也可能需要对子树进一步分割才能有最大的
}
//将该节点中的最大的计算结果存入本子树中
value[n]=res;
return value[n];
}
public int IntergerBreak3(int n) {//动态规划自下向上法
//
value[1]=1;
for (int i = 2; i < value.length; i++) {
for (int j = 1; j < i-1; j++) {
value[i]=max3(value[i],j*(i-j),j*value[i-j]);//将最大的结果存入子节点中
}
}
return value[value.length-1];
}
}