浅学动态规划

103 阅读4分钟

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

本文所有内容来自对动态规划相关内容的个人总结

本文内容是基于网络平台提供的知识点,感谢互联网和那些愿意分享知识的大佬们
希望能坚持下去

动态规划的解题思路

最后一步

顾名思义,这里的最后一步就是要我们去找出题目的“最后一步”,在常见的走楼梯问题中,最后一步就是走完楼梯选择的台阶数,在找零问题中最后一步就是达到指令金额用的最后一个硬币。

转移方程

转移方程是用数学语言对于解题思路的总结,动态规划的解题思路就是遍历比较,而遍历会存在边界,所以需要设置边界条件

边界条件

边界条件是最容易的,就是动态规划的前一步或者前两步的答案,一般稍加思考就能得出

动态规划的基本问题

动态规划一般是对一些求最优解、求方法数量,求存在性的问题的解决思路,比如最基础的找零问题、走楼梯问题等等,这些问题都有着一定的共性,我将结合一些例题来讲述我对动态规划的理解。

爬楼梯问题

这里引用力扣的两道爬楼梯题来入门

例题1 爬楼梯

假设你正在爬楼梯。需要n n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

在思考动态问题的时候,我们通常要发现其中存在的规律。 先假设爬到楼顶的方法为 f(n)f(n)(n为台阶数)

最后一步

这里的最后一步有可能是1个台阶或者2个台阶,那么我们可以得出结论:爬上n1n-1阶楼梯的方法数加上爬上n2n-2阶楼梯的方法数等于爬到楼顶(即nn阶)的方法数

转移方程

用数学语言表达出来就是 f(n)=f(n1)+f(n2)f(n)=f(n-1)+f(n-2)

边界条件

有了这个式子,我们只需要 f(1)f(1)f(2) f(2),就可以往后递推,求出对应任何台阶数的值。 而f(1)=1f(2)=2 f(1)=1,f(2)=2

三个要素都有了,思路已经很清晰了,我们就可以开始写代码了 代码实现如下:

class Solution {
    public int climbStairs(int n) {
        int[] dp=new int[n+1];
        //因为需要定义边界条件所以需要将边界条件对应的答案排除,避免出现异常       
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        dp[0]=0;
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

做完最简单的,那我们来为这道题目加一点变化

例题2 使用最小花费爬楼梯

数组的每个下标作为一个阶梯,第 ii 个阶梯对应着一个非负数的体力花费值cost[i] cost[i](下标从 0 开始)。

每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。

请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。 细节如图 先设爬到第n级阶梯的最小值为f(n)f(n)

最后一步

跟上一道题有一些区别,上一道题是求方法数,这一题是求最大值,所以这一题的最后一步是 爬到上一级阶梯的最小值加上体力花费值和上两级阶梯的最小值加上体力花费值之间的最小值

转移方程

根据最后一步可以得出转移方程为f(n)=min(f(n2)+cost[i],f[n1]+cost[i])f(n)=min(f(n-2)+cost[i],f[n-1]+cost[i])

边界条件

f(0)=cost[0],f(1)=min(cost[0],cost[1])f(0)=cost[0],f(1)=min(cost[0],cost[1]) 最终代码实现如下:

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int[] dp=new int[cost.length+1];
        dp[0]=dp[1]=0;
        for(int i=2;i<=cost.length;i++)
        {
            dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[cost.length];
    }
}

让我们换一种题来做一下

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 在这里插入图片描述

最后一步

这里因为不能偷相邻的房,所以就是取偷到前一间房的最大值和偷到这间房的最大值之间的最大值

转移方程

设偷到第n间房的最大值为f(n)f(n) 则转移方程为f(n)=max(f(n2)+nums[i]),f(n1))f(n)=max(f(n-2)+nums[i]),f(n-1))

边界条件

f(0)=nums[0],f(1)=max(nums[0],nums[1])f(0)=nums[0],f(1)=max(nums[0],nums[1])

最终代码如下

class Solution {
    public int rob(int[] nums) {
        int n=nums.length;
        int[] dp=new int[n];
        if(n==1)
        {
            return nums[0];
        }
        dp[0]=nums[0];
        dp[1]=Math.max(nums[0],nums[1]);
        for(int i=2;i<n;i++)
        {
            dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[n-1];
    }
}

先写这么多,看有没有空写后续吧。