【算法】动态规划

算法现状

1、科技公司面试必考算法
2、题目类型多,没有固定模板
3、难度属于中上
4、根据面试经验,一半失败的面试都与动态规划有关

基本概念

算法导论这本书是这样介绍这个算法的,动态规划与分治方法类似,都是通过组合子问题的解来来求解原问题的。再来了解一下什么是分治方法,以及这两者之间的差别,分治方法将问题划分为互不相交的子问题,递归的求解子问题,再将它们的解组合起来,求出原问题的解。而动态规划与之相反,动态规划应用与子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治方法会做许多不必要的工作,他会反复求解那些公共子子问题。而动态规划对于每一个子子问题只求解一次,将其解保存在一个表格里面,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作。

什么是动态规划?

如下图所示,题A为动态规划类型的题目,而题B却不是。
在这里插入图片描述

动态规划题目特点

在这里插入图片描述

相关问题举例

问题一(求最大最小值动态规划)

在这里插入图片描述
直觉做法
最少的硬币组合——>尽量使用面值大的硬币,最后如果可以用一种硬币付清就行
7+7+7=21
21+2+2+2=27
此处需要6枚硬币

但正确答案却不是这样的,而是:
7+5+5+5+5=27,需要5枚硬币。
动态规划解法
步骤一:确定状态

  • 状态在动态规划中的作用属于定海神针
  • 简单来说,解动态规划的时候需要解开一个数组,数组的每个元素f[i]或者f[i][j]代表什么
    ——类似于解数学题中,X、Y、Z代表什么
  • 确定状态需要两个意识
    ——最后一步
    ——子问题
    最后一步
    在这里插入图片描述
    关键点:
    在这里插入图片描述
    子问题
    在这里插入图片描述
    在这里插入图片描述
    由上可给出一个递归解法:
    在这里插入图片描述
    但上述递归算法存在问题:
    在这里插入图片描述
    如图可知,该递归解法做了很多重复计算,效率低下。

该如何避免这个问题了?我们可以将计算结果保存下来,并改变计算顺序,让我们接着往下看。
步骤二:状态转移方程
在这里插入图片描述
步骤三:初始条件和边界情况
在这里插入图片描述
步骤四:计算顺序
在这里插入图片描述
在这里插入图片描述

小结

在这里插入图片描述

leetcode例题

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。

链接:https://leetcode.cn/problems/gaM7Ch
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:
输入:coins = [2], amount = 3
输出:-1

示例 3:
输入:coins = [1], amount = 0
输出:0

示例 4:
输入:coins = [1], amount = 1
输出:1

示例 5:
输入:coins = [1], amount = 2
输出:2

提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

class Solution {
    public int coinChange(int[] coins, int amount) {
      int[] f=new int[amount+1];
      int n=coins.length;
      f[0]=0;
      for (int i=1;i<=amount;i++){
            f[i]=Integer.MAX_VALUE;
            for (int j=0;j<n;j++){
                if (i>=coins[j]&&f[i-coins[j]]!=Integer.MAX_VALUE){
                    f[i]=Math.min(f[i-coins[j]]+1,f[i]);
                }
            }
      }
      if (f[amount]==Integer.MAX_VALUE) {
          f[amount]=-1;
      }
      return f[amount];
    }
}

结果

在这里插入图片描述

问题二(计数)

在这里插入图片描述
步骤一:确定状态

最后一步在这里插入图片描述
子问题
在这里插入图片描述
步骤二:状态转移方程
在这里插入图片描述
步骤三:初始条件和边界情况
在这里插入图片描述
步骤四:计算顺序
在这里插入图片描述

leetcode例题

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

链接:https://leetcode.cn/problems/2AoeFn

示例 1:
在这里插入图片描述
输入:m = 3, n = 7
输出:28

示例 2:
输入:m = 3, n = 2
输出:3

示例 3:
输入:m = 7, n = 3
输出:28

示例 4:
输入:m = 3, n = 3
输出:6

提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109

class Solution {
    public int uniquePaths(int m, int n) {
          int[][] f=new int[m][n];
        f[0][0]=1;
        for (int i=0;i<m;i++){
            for (int j=0;j<n;j++){
                if (i==0||j==0){
                    f[i][j]=1;
                }else {
                    f[i][j]=f[i-1][j]+f[i][j-1];
                }
            }
        }
        return f[m-1][n-1];
    }
}

结果
在这里插入图片描述

问题三(求存在性)

在这里插入图片描述
步骤一:确定状态

最后一步
在这里插入图片描述
子问题
在这里插入图片描述
步骤二:转移方程
在这里插入图片描述
步骤三:初始条件和边界情况
在这里插入图片描述
步骤四:计算顺序
在这里插入图片描述

leetcode例题

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。

链接:https://leetcode.cn/problems/jump-game/

示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105

class Solution {
    public boolean canJump(int[] nums) {
        boolean[] f=new boolean[nums.length];
        f[0]=true;
        for (int i=1;i<nums.length;i++){
            f[i]=false;
           for (int j=0;j<i;j++){
                if (nums[j]>=i-j&&f[j])
                    f[i]=true;
           }
        }
        return f[f.length-1];
    }
}

结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值