动态规划常用方法以及例题(Dynamic Programing)②

一、引入

前面简单的讨论了动态规划的基本原理,以及实现步骤,当然简单的理论是完全不够的,现在我们就从一个我们很早就接触过的 斐波那契数列(Fibonacci Sequence) 开始来引入动态规划的基本实现思路。

二、问题一(斐波那契数列)

这是我们常见的一个斐波那契数列
在这里插入图片描述
斐波那契的递推公式
动态规划常用方法以及例题(Dynamic Programing)②_第1张图片
这里我们假设求斐波那契第六个数字,那么他的递归图如下,我们可以从图中发现在递归中会重读的计算某个值,也就是说存在子问题重叠(Overlap - Sub-Problem),由于计算机是一个“死板”的东西,他不会说前面计算过那么后面就不用计算,从图中可以看出递归的时间复杂度为O(2^n)。

动态规划常用方法以及例题(Dynamic Programing)②_第2张图片
那么我们如何用动态规划的思想求解该问题?
  动态规划中主要的就是将前面的计算的数值放在内存中,那么当我们后面用的时候就从内从中取出来,从而不用继续计算前面已经计算过的值。
动态规划常用方法以及例题(Dynamic Programing)②_第3张图片
在上面的图中,我们求8的时候,会找前面的5和3,同理求5的时候需要找前面的2和3,使用动态规划我们只需要将前面计算出来的值进行保存,后面用的时候取出来就可以,从而降低时间复杂度。
状态转移方程

这里的状态转移方程和前面的递归方程一样,出口也一样。
动态规划常用方法以及例题(Dynamic Programing)②_第4张图片
现在先看递归版本

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个下标),之前,只要是不相邻的数字可以组成的最大数字是多少。
动态规划常用方法以及例题(Dynamic Programing)②_第5张图片
动态规划和递归不一样,一般是从后向前推。
思路
我们现在建立一个数组opt[ ], opt[ i ]代表的是到下标i为止的最佳方案。 那么现在问题就转换成选与不选的问题。
动态规划常用方法以及例题(Dynamic Programing)②_第6张图片
那么我们可以推算出出口在哪里?

  • 第一种:当下标 i = 0,opt[0] = arr[0]
  • 第二种:当下标 i=1时,opt[1] = Max(arr[0],arr[])

动态方程
动态规划常用方法以及例题(Dynamic Programing)②_第7张图片
递归版代码

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

你可能感兴趣的:(算法)