一、引入
前面简单的讨论了动态规划的基本原理,以及实现步骤,当然简单的理论是完全不够的,现在我们就从一个我们很早就接触过的 斐波那契数列(Fibonacci Sequence) 开始来引入动态规划的基本实现思路。
二、问题一(斐波那契数列)
这是我们常见的一个斐波那契数列
斐波那契的递推公式
这里我们假设求斐波那契第六个数字,那么他的递归图如下,我们可以从图中发现在递归中会重读的计算某个值,也就是说存在子问题重叠(Overlap - Sub-Problem),由于计算机是一个“死板”的东西,他不会说前面计算过那么后面就不用计算,从图中可以看出递归的时间复杂度为O(2^n)。
那么我们如何用动态规划的思想求解该问题?
动态规划中主要的就是将前面的计算的数值放在内存中,那么当我们后面用的时候就从内从中取出来,从而不用继续计算前面已经计算过的值。
在上面的图中,我们求8的时候,会找前面的5和3,同理求5的时候需要找前面的2和3,使用动态规划我们只需要将前面计算出来的值进行保存,后面用的时候取出来就可以,从而降低时间复杂度。
状态转移方程
这里的状态转移方程和前面的递归方程一样,出口也一样。
现在先看递归版本
import java.util.Scanner;
public class feibo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要查询的第n个斐波那契数");
int n = sc.nextInt();
int num = Get_Num(n);
System.out.println("你查询的第"+n+"个斐波那契数为"+num);
}
public static int Get_Num(int i){
if (i == 1||i == 2){
return 1;
}else {
return Get_Num(i - 1) + Get_Num(i - 2);
}
}
}
动态规划版本
import java.util.Scanner;
public class dp_feobo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要查询的第n个斐波那契数");
int n = sc.nextInt();
int num = Get_Num(n);
System.out.println("你查询的第"+n+"个斐波那契数为"+num);
}
public static int Get_Num(int n){
int[] opt = new int[n];//将计算的值放进opt数组中,后面用拿就可以
opt[0] = 1;
opt[1] = 1;
for (int i = 2;i < n;i++){
opt[i] = opt[i - 1] + opt[i - 2];
}
return opt[n - 1];
}
}
三、问题二
给你一个int类型数组,现在要求让你设计一个算法求出到第i个下标(包括第i个下标),之前,只要是不相邻的数字可以组成的最大数字是多少。
动态规划和递归不一样,一般是从后向前推。
思路
我们现在建立一个数组opt[ ], opt[ i ]代表的是到下标i为止的最佳方案。 那么现在问题就转换成选与不选的问题。
那么我们可以推算出出口在哪里?
- 第一种:当下标 i = 0,opt[0] = arr[0]
- 第二种:当下标 i=1时,opt[1] = Max(arr[0],arr[])
动态方程:
递归版代码
import java.util.Scanner;
public class dg_arr {
public static void main(String[] args) {
int[]arr = {1,2,4,1,7,8,3};
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要查询第i个小标的最优解");
int num = sc.nextInt();
int num1 = getBiggestNum(arr, num);
System.out.println("到第i个节点之前的最大和为"+num1);
}
public static int getBiggestNum(int[]arr,int i){
int A;
int B;
if (i== 0){
return arr[0];
}else if (i == 1){
return Math.max(arr[0],arr[1]);
}else {
A = getBiggestNum(arr,i - 2)+arr[i];//选第i个
B = getBiggestNum(arr,i - 1);//不选第i个
return Math.max(A,B);
}
}
}
动态规划版代码
import java.util.Scanner;
public class dp_arr {
public static void main(String[] args) {
int[]arr = {4,1,1,9,1};
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要查询第"+"i个小标的最优解");
int n = sc.nextInt();
int num1 = getBiggestNum(arr, n);
System.out.println("到第i个节点之前的最大和为"+num1);
}
public static int getBiggestNum(int[]arr,int n){
int A;
int B;
int[] opt = new int[n+1];//创建一个临时数组,将前面计算出的值放在里面,这里记得数组的大小为你求的小标加上1
opt[0] = arr[0];
opt[1] = Math.max(arr[0],arr[1]);
for (int i=2;i <= n;i++){
A = opt[i - 2] + arr[i];
B = opt[i - 1];
opt[i] = Math.max(A,B);
}
return opt[n];
}
}
请输入你要查询第i个小标的最优解
4
到第i个节点之前的最大和为13
Process finished with exit code 0