秋招-算法-动态规划篇

2023-11-13

秋招-算法-动态规划篇

只求秋招笔试能过,所以本文更多是怎么使用模板来解动态规划题,能过就好,对时间和空间的考虑不会太多

介绍

动态规划通过组合子问题的解得到原问题的解。 适合动态规划解决的问题具有重叠子问题和最优子结构两大特征,通常使用空间换时间的办法。

  • 重叠子问题

    动态规划的子问题具有重叠的,即各个子问题中包含重复的更小的子问题。若使用暴力法进行穷举,求解这些相同子问题会查收大量的重复计算,效率抵下。动态规划在第一次求解某个子问题时,会将子问题的解保存至矩阵中,后续遇到子问题时,则直接通过查表获取解,保证每个独立子问题制备计算一次,从而降低算法的时间复杂度。

  • 最优子结构
    如果一个问题的最优解可以由其子问题的最优解组合构成,那么称此问题具有最优解结构。动态规划从基础问题的解开始,不断进行迭代组合、选择子问题的最优解,最终得到原问题的最优解。

解题模板

  1. 明确 base case,将基本返回写出。(题目看不出来可以先跳过)
      //明确 base case
      if (amount == 0) return 0;
      if (amount < 0) return -1;
  1. 明确「状态」(怎么将大问题变为小问题,或者说小问题怎么通过组合,加1等方式得到大问题的结果),进行递归,题目一般会问包含最大,最小等关键字,这一步不用管最大最小,只要考虑小问题怎么通过组合,加1等方式得到大问题的结果
dp(param)=dp(param-1) + 1;
  1. 明确「选择」(获得子问题的解),从多个小问题中获取最值
min(
          //明确「状态」,将小问题的解转化为大问题的解
                dp(param+1),
                dp(param-1),
        ) + 1;
  1. 将子问题的解存储用于之后的子问题,数组之类的存储,一般这个缓存习惯称为memo。
//一般对memo有查询和插入两个操作

//查询,查看备忘录memo有没有相关数据,有就不用做了直接return
      if (memo[amount] != 0)
         return memo[amount];

//插入将子问题最优解保存
memo[point1][point2] = min(
          //明确「状态」,将小问题的解转化为大问题的解
                dp(param+1),
                dp(param-1),
        ) + 1;

按上面的套路走,最后的解法代码就会是如下的框架:

class Solution {
     int[]memo;
    public int change(param) {
        memo = new int[len];
        return dp(coins,amount);
    }

    int dp(param) {
      //明确 base case
      if (amount == 0) return 0;
      if (amount < 0) return -1;
		//查看备忘录memo有没有相关数据,有就不用做了直接return
      if (memo[amount] != 0)
         return memo[amount];
		//没有就递归选取子问题的解,并将该值保存
      return memo[point1][point2] = min(
          //明确「状态」,将小问题的解转化为大问题的解
                dp(param+1),
                dp(param-1),
        ) + 1;
    }
}

例题

正常的动态规划

322. 零钱兑换

image-20220726222849898

  1. 明确 base case ==> amount=0 输出 0, amount和coins不匹配 result = -1

  2. 明确「状态」(怎么将大问题变为小问题) > 要求amount11时的硬币个数,可以求amount = 11-1、11-2、11-5的硬币个数然后加一

    这里通过递归调用进行分解。

  3. 明确「选择」(获得子问题的解) ==> 和当前存储的值进行比较,取最小的数值

coinChange(11) = Min(coinChange(11-1),coinChange(11-2),coinChange(11-5))+1

  1. 将子问题的解存储用于之后的子问题。
class Solution {
     int[]memo;
    public int coinChange(int[] coins, int amount) {
        memo = new int[amount+1];
        Arrays.fill(memo, -666);
        dp(coins,amount);
        return dp(coins,amount);
    }

    int dp(int[] coins, int amount) {
      //明确 base case
      if (amount == 0) return 0;
      if (amount < 0) return -1;

      if (memo[amount] != -666)
         return memo[amount];

      int res =  Integer.MAX_VALUE;
      for(int i=0;i<coins.length;i++){
        //明确「状态」
        int sub = dp(coins,amount-coins[i]);
        if (sub == -1) continue;
          //明确「选择」
         res = Math.min(res, sub+1 );
        }
        //将子问题的解存储用于之后的子问题。
        memo[amount] = (res == Integer.MAX_VALUE) ? -1 : res;
        return memo[amount];
    }
}
  • 最后通过

image-20220728234613540

比较难的动态规划

72. 编辑距离

image-20220728231727788

题目比较难,看不出base case和memo数组怎么用,先从状态转移入手

  • 状态转移

求"horse"到"ros"的操作数,就是求 替换"horse"的最小操作数 +1 或者 删除"horse"的最小操作数+1 或者 增加"horse"的最小操作数+1

  • 选择

用point1代表str1的索引,用point2代表str2的索引,int trans(int point1,int point2)代表从point1开始的字符串变换到从point2开始的字符串要的最小操作数

替换"horse"–>“rorse”,“ros” :trans(point1+1,point2+1),

删除"horse"–>“orse”,“ros”:trans(point1+1,point2),

增加"horse"–>“rhorse”,“ros” :trans(point1,point2+1)

求"horse"到"ros"的最小操作数,就是求min( 替换"horse"的最小操作数 ,删除"horse"的最小操作数,增加"horse"的最小操作数 )+1

综合 转移和选择,可以写出:

min(
                //替换 "rorse","ros"
                trans(point1+1,point2+1),
                //删除 "orse","ros"
                trans(point1+1,point2),
                //增加 "rhorse","ros"
                trans(point1,point2+1)
        ) + 1;
  • base case

然后考虑边界情况,得到三个base case

        //base case
        //str1太短,补全str2剩余部分的长度
        if (point1==str1.length()){
            return str2.length()-point2;
        }
        //str1太长,删除多余部分
        if (point2==str2.length()){
            return str1.length()-point1;
        }
		//str1和str2在当前位置相同
        if (str1.charAt(point1)==str2.charAt(point2)){
            return trans(point1+1,point2+1);
        }else return min(
                //替换 "rorse","ros"
                trans(point1+1,point2+1),
                //删除 "orse","ros"
                trans(point1+1,point2),
                //增加 "rhorse","ros"
                trans(point1,point2+1)
        ) + 1;
  • 补全代码获得暴力解法
public class QuickTest {
    public static void main(String[] args) {
        System.out.println(new Solution().minDistance("horse","ros"));
    }
}

class Solution {
    String str1;
    String str2;
    public int minDistance(String word1, String word2) {
        str1 = word1;  str2 = word2;
        // 状态转移  "horse","ros"
        // trans(i,j) = (min(trans(i+1,j+1) 【i+1,j+1代表由于操作所降低的操作空间】))+ 1【代表一次操作】
        return trans(0,0);
    }

    int trans(int point1,int point2){

        //base case
        //str1太短,补全str2剩余部分的长度
        if (point1==str1.length()){
            return str2.length()-point2;
        }
        //str1太长,删除多余部分
        if (point2==str2.length()){
            return str1.length()-point1;
        }

        if (str1.charAt(point1)==str2.charAt(point2)){
            return trans(point1+1,point2+1);
        }else return min(
                //替换 "rorse","ros"
                trans(point1+1,point2+1),
                //删除 "orse","ros"
                trans(point1+1,point2),
                //增加 "rhorse","ros"
                trans(point1,point2+1)
        ) + 1;

    }

    int min(int x,int y,int z){
        return Math.min(x,Math.min(y,z));
    }

}

  • 为暴力解法添加memo备忘录
import java.util.Arrays;

public class QuickTest {
    public static void main(String[] args) {
        System.out.println(new Solution().minDistance("intention","execution"));
    }
}

class Solution {
    String str1;
    String str2;
    int [][] memo;
    public int minDistance(String word1, String word2) {
        str1 = word1;  str2 = word2;
        //设置备忘录
        memo = new int[word1.length()+1][word2.length()+1];

        // 状态转移  "horse","ros"
        // trans(i,j) = (min(trans(i+1,j+1) 【i+1,j+1代表由于操作所降低的操作空间】))+ 1【代表一次操作】
        return trans(0,0);
    }

    int trans(int point1,int point2){

        //base case
        //str1太短,补全str2剩余部分的长度
        if (point1==str1.length()){
            return str2.length()-point2;
        }
        //str1太长,删除多余部分
        if (point2==str2.length()){
            return str1.length()-point1;
        }

        //查询备忘录
        if (memo[point1][point2]!=0){
            return memo[point1][point2];
        }

        if (str1.charAt(point1)==str2.charAt(point2)){
            return memo[point1][point2] = trans(point1+1,point2+1);
        }else return memo[point1][point2] = min(
                //替换 "rorse","ros"
                trans(point1+1,point2+1),
                //删除 "orse","ros"
                trans(point1+1,point2),
                //增加 "rhorse","ros"
                trans(point1,point2+1)
        ) + 1;

    }

    int min(int x,int y,int z){
        return Math.min(x,Math.min(y,z));
    }

}
  • 最后通过

image-20220728234206198

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

秋招-算法-动态规划篇 的相关文章

随机推荐

  • Redis持久化的原理及优化

    更多内容 欢迎关注微信公众号 全菜工程师小辉 Redis提供了将数据定期自动持久化至硬盘的能力 包括RDB和AOF两种方案 两种方案分别有其长处和短板 可以配合起来同时运行 确保数据的稳定性 RDB 保存数据快照至一个RDB文件中 用于持久
  • An HTTP error occurred when trying to retrieve this URL.

    问题背景 conda install xxx 提示 An HTTP error occurred when trying to retrieve this URL Fetching package metadata CondaHTTPErr
  • 【Leetcode】863. 二叉树中所有距离为 K 的结点

    题目描述 题解 用map记录每个结点的父结点 然后让dfs从target结点开始 假设target就是根结点 然后递归时纪录深度 只要深度等于k 就是和target的距离等于k 就可以存入list 执行用时 14 ms 在所有 Java 提
  • LeetCode-1304. Find N Unique Integers Sum up to Zero

    Given an integer n return any array containing n unique integers such that they add up to 0 Example 1 Input n 5 Output 7
  • 毕业遭失业,前途一片黑暗...不得已转行软件测试,太多心酸和无助...

    大家好 我叫小涵 一名应届毕业生 目前已经成功转行互联网 写这篇文章的目的是因为很多人不喜欢自己的现状 想通过学习改变 奈何没有出路 所以想为这部分人提供一些思路 其次文章会总结我自己转行前后的经历和思考 提供一些我亲测有效的面试资源 找不
  • 解决Vmware Unbuntu 22虚拟机网络故障问题

    上午启动Vmware Unbuntu 22虚拟机 发现不能上网 屏幕右上侧的网络图标没有显示 怀疑是昨天在虚拟机上做路由器功能设置的时候某个操作产生的问题 于是进行排障 先尝试重启网络服务 service NetworkManager re
  • elasticsearch安装 及 启动异常解决

    虚拟机使用net连接模式 1 Download and unzip the latest Elasticsearch distribution 2 Run bin elasticsearch on Unix or bin elasticse
  • 第14课 右值引用(1)_基本概念

    1 左值和右值 1 两者区别 左值 能对表达式取地址 或具名对象 变量 一般指表达式结束后依然存在的持久对象 右值 不能对表达式取地址 或匿名对象 一般指表达式结束就不再存在的临时对象 2 右值的分类 将亡值 xvalue eXpiring
  • 数据中台-让数据用起来-7

    文章目录 第七章 数据体系建设 7 1 数体系规划 7 2 贴源数据层建设 全域数据统一存储 7 2 1 相关概念 7 2 2 贴源数据表设计 7 2 3 贴源数据表实现 7 3统一数仓层建设 标准化的数据底座 7 3 1 相关概念 7 3
  • Java高阶面试问答-通用基础

    线程和进程区别 进程是资源分配的最小单位 线程是程序执行的最小单位 进程有自己的独立地址空间 每启动一个进程 系统就会为它分配地址空间 建立数据表来维护代码段 堆栈段和数据段 线程是共享进程的数据空间 因此CPU切换一个线程的花费远比进程要
  • LeetCode-1775. 通过最少操作次数使数组的和相等【贪心,数组,计数】

    LeetCode 1775 通过最少操作次数使数组的和相等 贪心 数组 计数 题目描述 解题思路一 让sum1
  • kubernetes pv回收策略

    本文最近更新于2021 9 11 kubernetes pv回收策略 当用户不再使用其存储卷时 他们可以从 API 中将 PVC 对象删除 从而允许该资源被回收再利用 PersistentVolume 对象的回收策略告诉集群 当其被从申领中
  • 冈萨雷斯《数字图像处理》学习总结及感悟:第一章 绪论 百闻不如一见

    前往老猿Python博文目录 https blog csdn net LaoYuanPython 一 引言 好几月前开始自学OpenCV Python 但老猿以前没接触过图像基础知识 数学知识基本上也都忘光了 因此在自学OpenCV Pyt
  • Python处理txt数据实例

    现在有一个具体的案例是这样的 CST电磁仿真软件得到一些txt数据在origin data文件夹中 需要其中的一些数据来通过origin软件绘制曲线分析一些问题 而且需要里面的所有数据曲线显示在同一个图形中 如果通过手动将txt数据一一复制
  • LeetCode第55题解析

    给定一个非负整数数组 你最初位于数组的第一个位置 数组中的每个元素代表你在该位置可以跳跃的最大长度 判断你是否能够到达最后一个位置 示例 1 输入 2 3 1 1 4 输出 true 解释 我们可以先跳 1 步 从位置 0 到达 位置 1
  • js的闭包的理解

    js的变量的作用域分为全局变量和局部变量 函数内部的变量称为局部变量 在函数的内部可以访问到全局变量 但是函数外部无法访问函数内部的变量 闭包可以解决无法访问函数内部的变量的问题 且可以隐藏这个变量 不被外部直接访问 闭包 函数内部的子函数
  • JavaScript 搜索引擎 lunr.js

    lunr js 实现了在网页上的搜索引擎 类似 Solr 示例代码 view source print 01 定义索引 02 var idx lunr function 03 this field title boost 10 04 thi
  • flask需求文件requirements.txt的创建和使用

    flask需求文件requirements txt的创建及使用 简介 flask项目中包含一个requirements txt 文件 用于记录所有依赖包及其精确的版本号用以新环境部署 创建 生成需求文件 在命令行输入 pip freeze
  • 服务器一直被攻击怎么办?

    有很多人问说 网站一直被攻击 什么被挂马 什么被黑 每天一早打开网站 总是会出现各种各样的问题 这着实让站长们揪心 从修改服务器管理账号开始 到修改远程端口 什么措施都做了 还是会被攻击挂马 服务器一直被攻击时 要怎么做 1 切断网络 对服
  • 秋招-算法-动态规划篇

    秋招 算法 动态规划篇 只求秋招笔试能过 所以本文更多是怎么使用模板来解动态规划题 能过就好 对时间和空间的考虑不会太多 介绍 动态规划通过组合子问题的解得到原问题的解 适合动态规划解决的问题具有重叠子问题和最优子结构两大特征 通常使用空间