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

一、引入

前面简单的讨论了动态规划的基本原理,以及实现步骤,当然简单的理论是完全不够的,现在我们就从一个我们很早就接触过的 斐波那契数列(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为止的最佳方案。 那么现在问题就转换成选与不选的问题。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190828133322496.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1cHRfcmw=,size_16,color_FFFFFF,t_70
那么我们可以推算出出口在哪里?

  • 第一种:当下标 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值