动态规划入门(Java)

动态规划介绍

动态规划通过组合子问题的解来求解原问题,一般来说,动态规划应用于重叠子问题的情况,即不同的子问题具有公共的子子问题。动态规划算法对每个子子问题只求解一次,将其解保存再一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了这种不必要的计算工作。
动态规划有两种等价的实现方法

  1. 第一种方法称为带备忘的自顶向下法。此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存再一个数组或散列表中)。当需要一个子问题的解时,过程首先检查释放已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。我们称这个递归过程是带备忘的,因为它"记住"了之前已经计算出的结果
  2. 第二种方法称为自底向上法。这种方法一般需要恰当定义子问题"规模"的概念,使得任何子问题的求解都只依赖于"更小的"子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次,当我们求解它(也是第一次遇到它)时,它的所有前提子问题都已求解完成。

动态规划中的常见的的概念

这里我们以求解斐波那契数列来举例子

  1. 子问题和原问题

    • 原问题就是你要求解的这个问题本身,子问题是和原问题相似但规模较小的问题(原问题本身就是子问题的最复杂的情形,即子问题的特例)
    • 例如:要求F(10),那么求出F(10)就是原问题,求出F(K)(k<=10)都是子问题
  2. 状态就是子问题中会变化的某个量,可以把状态看成我们要求解的问题的自变量。

    • 例如: 我们要求的F(10),那么这里的自变量10就是一个状态.
  3. 状态转移方程

    • 例如: F(n) = F(n-1)+F(n-2),当n为>2的整数时;当n=1或2时,F(n)=1,这种最简单的初始条件一般称为边界条件,也被称为基本方程。
  4. DP数组(DP就是动态规划的缩写)

    • DP数组也可以叫"子问题数组",因为DP数组中的每一个元素都对应一个子问题的结果,DP数组的下标一般就是该子问题对应的状态。
    • 例如L 使用自底向上法编程求解时,我们定义的向量FF就可以看成一个DP数组,数组下标从1取到n,对应的元素从F(1)取到F(n);

这些基础概念全部来自清风数学建模课件。

说了这么多基础概念,接下来我们就用打家劫舍这道题,一步步用动态规划来进行求解吧。

打家劫舍

题目

在这里插入图片描述
动态规划的四个解题步骤是:

  • 定义子问题
  • 写出子问题的递推关系
  • 确定DP数组的计算顺序

定义子问题

比如这道题目,原问题:是求“从全部房子中能够偷到的最大金额”,那么子问题就是“从前k个房子中能偷到的最大金额”
.子问题需要具备以下两个性质

  • 原问题要能由子问题表示
  • 一个子问题的解要能通过其他子问题的解求出.

写出子问题的递推关系

假设有4个房子,每个房子的金额分别是 1、2、3、1, 那么,偷前k个房子有两种偷法

  • 方案一: 偷前k-1间房子,最后一间不偷
  • 方案二: 偷前k-2间房子和最后一间。

情况(1): 当k=1时,只有第1间房子可以偷,因此f(1)应该等于这间房子的金额
情况(2): 当k=2时,只有第一间房子和第二间房子可以偷,又因为小偷不可以偷两个相邻的房子,因此我们只能选择金额较大的那间房子偷
注意: 这里情况(1)和情况(2)就是动态规划问题中的边界条件,也称为基本方程。

情况(3): 当K>=3时,分两种情况讨论

  1. 小偷已经偷了第k-1间房子,这时候小偷不能偷第k间房子,此时最多能获取f(k-1)的钱财
  2. 小偷没有偷第k-1间房子,根据定义小偷再前k-2间房子能够偷到的最大钱财为f(k-2),又因为没有偷第k-1间房子,那么小偷一定会去偷第k间房,此时最多能获得f(k-2)+Mk的钱财
  3. 得出状态转移方程
    在这里插入图片描述

确定DP数组的计算顺序

DP[k]记录了小偷能从前k间房子中偷到的最大金额,DP[K]的求解依赖于DP[k-1]和DP[k-2]

代码如下

 public  static  int rob(int[] arr){
        if(arr.length == 0){
            return 0;
        }else if(arr.length==1){
            return  arr[0];
        }else if(arr.length==2){
            return Math.max(arr[0],arr[1]);
        }else{
            //创建DP数组
            int[] dp = new int[arr.length];
            dp[0] = arr[0];
            dp[1] = Math.max(arr[0],arr[1]);
            for (int i = 2; i <arr.length; i++) {
                dp[i] = Math.max(dp[i-1],dp[i-2]+arr[i]);
            }
            System.out.println(Arrays.toString(dp));
            return dp[arr.length-1];
        }
    }

参考: 数学建模清风动态规划.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值