(转载自 极客时间 )

今天我们不谈具体的解决方案了,我们来聊聊面试相关的话题,做个总结,也为我们后面的深入学习打下一个良好的基础。

那说起动态规划,我不知道你有没有这样的困扰,在掌握了一些基础算法和数据结构之后,碰到一些较为复杂的问题还是无从下手,面试时自然也是胆战心惊。如果我说动态规划是个玄幻的问题其实也不为过。究其原因,我觉得可以归因于这样两点:

你对动态规划相关问题的套路和思想还没有完全掌握;

你没有系统地总结过究竟有哪些问题可以用动态规划解决。

知己知彼,你想把动态规划作为你的面试武器之一,就得足够了解它;而应对面试,总结、归类问题其实是个不错的选择,这在我们刷题的时候其实也能感觉得到。

那么今天,我们就针对以上两点,系统地谈一谈究竟什么样的问题可以用动态规划来解。相信这节课过后,你就能有针对性地攻克难关了,无论是面试还是工程实践都能做到有的放矢。

动态规划是一种思想

动态规划算法,这种叫法我想你应该经常听说。嗯,从道理上讲这么说我觉得也没错,首先动态规划它不是数据结构,这一点毋庸置疑,并且严格意义上来说它就是一种算法。但更加准确或者更加贴切的提法应该是说动态规划是一种思想

那什么是思想?算法和思想又有什么区别呢?

一般来说,我们都会把算法和数据结构放一起来讲,这是因为它们之间密切相关,而算法也往往是在特定数据结构的基础之上对解题方案的一种严谨的总结

比如说,在一个乱序数组的基础上进行排序,这里的数据结构指的是什么呢?很显然是数组,而算法则是所谓的排序。至于排序算法,你可以考虑使用简单的冒泡排序或效率更高的快速排序方法等等来解决问题。

没错,你应该也感觉到了,算法是一种简单的经验总结和套路。那什么是思想呢?相较于算法,思想更多的是指导你我来解决问题。

比如说,在解决一个复杂问题的时候,我们可以先将问题简化,先解决简单的问题,再解决难的问题,那么这就是一种指导解决问题的思想。另外,我们常说的分治也是一种简单的思想,当然它在诸如归并排序或递归算法当中会常常被提及。

而动态规划就是这样一个指导我们解决问题的思想:你需要利用已经计算好的结果来推导你的计算,即大规模问题的结果是由小规模问题的结果运算得来的。这句话对于你充分理解动态规划的基本原理十分重要,希望你能记下来。

简单理解的话,你可以这样认为:算法是一种经验总结,而思想则是用来指导我们解决问题的

既然动态规划是一种思想,那它实际上就是一个比较抽象的概念了,也很难和实际的问题关联起来。所以说,弄清楚什么样的问题可以使用动态规划来解,就显得十分重要了。

动态规划问题的典型特点

在前几课中,我们已经基本了解了动态规划的基本概念,从贪心算法、暴力递归再到最后的动态规划解法,我们比较完美地解决了提出的问题。在这个过程中,你有没有想过这些问题为什么可以用动态规划来解决?是别人这么做我也要这么做吗?别人的经验又是从何而来?

事实上,动态规划是运筹学上的一种最优化方法,只不过在算法问题上应用广泛。接下来我们就深挖一层,看看动归问题所具备的一些特点。

求“最”优解问题(最大值和最小值)

除非你碰到的问题是简单到找出一个数组中最大的值这样,对这种问题来说,你可以对数组进行排序,然后取数组头或尾部的元素,如果觉得麻烦,你也可以直接遍历得到最值。不然的话,你就得考虑使用动态规划来解决这个问题了。这样的问题一般都会让你求最大子数组、求最长递增子数组、求最长递增子序列或求最长公共子串、子序列等等。不知道你发现没有,这些问题里都包含一个“最”字,如果出现了这个字,那么你就该警惕它是否是动归问题。那具体怎么判断呢?

既然是要求最值,不妨先想一下核心问题是什么。其实在真的解决最值问题的时候,你应该按照这样的思考顺序来解决问题

优先考虑使用贪心算法的可能性

然后是暴力递归进行穷举(但这里的数据规模不大);

还是不行呢?选择动态规划!

你也看到了,求解动态规划的核心问题其实就是穷举。那么因为我们要求最值,就肯定要把所有可行的答案穷举出来,然后在其中找最值就好了嘛。你看,动态规划也不过如此,就两个字:穷举。

当然了,动态规划问题也不会这么简单了事,我们还需要考虑待解决的问题是否存在重叠子问题、最优子结构等特性。我们已经在前面的课程中讲清楚了重叠子问题,而对于最优子结构,我会在后续的内容中继续给你讲清楚。

清楚了特点,那根据我的经验,绝大多数面试者其实还是很难在第一时间针对具体问题采取明确的行动:这个问题到底该不该用动态规划思想来解呢?

我在这里列出几道常见的经典面试题,如果你遇到它们了,不要犹豫,使用动态规划来解。这样不仅目标明确,而且会在面试时给面试官留下极为深刻的印象(这一讲我们只是分析下题目,后面的课程中会涵盖所有问题的解法,并归纳总结解题套路)。

1. 乘积最大子数组

问题:给你一个整数数组 numbers,找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),返回该子数组的乘积。

示例1:输入: \[2,7,-2,4\]输出: 14解释: 子数组 \[2,7\] 有最大乘积 14。示例2:输入: \[-5,0,3,-1\]输出: 3解释: 结果不能为 15, 因为 \[-5,3,-1\] 不是子数组,是子序列。

首先,很明显这个题目当中包含一个“最”字,使用动态规划求解的概率就很大。这个问题的目的就是从数组中寻找一个最大的连续区间,确保这个区间的乘积最大。由于每个连续区间可以划分成两个更小的连续区间,而且大的连续区间的结果是两个小连续区间的乘积,因此这个问题还是求解满足条件的最大值,同样可以进行问题分解,而且属于求最值问题。同时,这个问题与求最大连续子序列和比较相似,唯一的区别就是你需要在这个问题里考虑正负号的问题,其它就相同了。

2. 最长回文子串

问题:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

这个问题依然包含一个“最”字,同样由于求解的最长回文子串肯定包含一个更短的回文子串,因此我们依然可以使用动态规划来求解这个问题。

3. 最长上升子序列

问题:给定一个无序的整数数组,找到其中最长上升子序列的长度。可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。

示例:输入: \[10,9,2,5,3,7,66,18\]输出: 4解释: 最长的上升子序列是 \[2,3,7,66\],它的长度是 4。

这个问题依然是一个最优解问题,假设我们要求一个长度为 5 的字符串中的上升自序列,我们只需要知道长度为 4 的字符串最长上升子序列是多长,就可以根据剩下的数字确定最后的结果。

求可行性(True 或 False)

接下来我们再来看另一种可能的动态规划问题。

如果有这样一个问题,让你判断是否存在一条总和为 x 的路径(如果找到了,就是 True;如果找不到,自然就是 False),或者让你判断能否找到一条符合某种条件的路径,那么这类问题都可以归纳为求可行性问题,并且可以使用动态规划来解。比如我们前面课程中提到的找零钱问题,是不是就很好地说明了这一点?

1. 凑零兑换问题

问题:给你 k 种面值的硬币,面值分别为 c1, c2 … ck,每种硬币的数量无限,再给一个总金额 amount,问你最少需要几枚硬币凑出这个金额,如果不可能凑出,算法返回 -1 。

示例1:输入: c1=1, c2=2, c3=5, c4=7, amount = 15输出: 3解释: 11 = 7 + 7 + 1。示例2:输入: c1=3, amount =7输出: -1解释: 3怎么也凑不到7这个值。

这个问题显而易见,如果不可能凑出我们需要的金额(即 amount),最后算法需要返回 -1,否则输出可能的硬币数量。这是一个典型的求可行性的动态规划问题。

2. 字符串交错组成问题

问题:给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。

示例1:输入: s1="aabcc",s2 ="dbbca",s3="aadbbcbcac"输出: true解释: 可以交错组成。示例2:输入: s1="aabcc",s2="dbbca",s3="aadbbbaccc"输出: false解释:无法交错组成。

这个问题稍微有点复杂,但是我们依然可以通过子问题的视角,首先求解 s1 中某个长度的子字符串是否由 s2 和 s3 的子字符串交错组成,直到求解整个 s1 的长度为止,也可以看成一个包含子问题的最值问题。

求方案总数

除了求最值与可行性之外,求方案总数也是比较常见的一类动态规划问题。比如说给定一个数据结构和限定条件,让你计算出一个方案的所有可能的路径,那么这种问题就属于求方案总数的问题。我在这里介绍几个典型例子,帮助你理解。

1. 硬币组合问题

问题:英国的英镑硬币有 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p), 和 £2 (200p)。比如我们可以用以下方式来组成 2 英镑:1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p。问题是一共有多少种方式可以组成 n 英镑? 注意不能有重复,比如 1 英镑 +2 个 50P 和 50P+50P+1 英镑是一样的。

这个问题本质还是求满足条件的组合,只不过这里不需要求出具体的值或者说组合,只需要计算出组合的数量即可。

2. 路径规划问题

问题:一个机器人位于一个 m x n 网格的左上角。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角,共有多少路径?

这个问题还是一个求满足条件的组合数量的问题,只不过这里的组合变成了路径的组合。我们可以先求出长宽更小的网格中的所有路径,然后再在一个更大的网格内求解更多的组合。这和硬币组合的问题相比没有什么本质区别。

这里有一个规律或者说现象需要强调,那就是求方案总数的动态规划问题一般都指的是求“一个”方案的所有具体形式。如果是求“所有”方案的具体形式,那这种肯定不是动态规划问题,而是使用传统递归来遍历出所有方案的具体形式。

为什么这么说呢?因为你需要把所有情况枚举出来,大多情况下根本就没有重叠子问题给你优化。即便有,你也只能使用备忘录对遍历进行一个简单加速。但本质上,这类问题不是动态规划问题。接下来你就会看到这样的例子,你可以找找区别在哪里。

进一步确认是否为动态规划问题

从前面我所说来看,如果你碰到了求最值、求可行性或者是求方案总数的问题的话,那么这个问题就八九不离十了,你基本可以确定它就需要使用动态规划来解。但这里还有一些极具迷惑性的问题,你需要格外注意

数据不可排序(Unsortable)

假设我们有一个无序数列,希望求出这个数列中最大的两个数字之和。很多初学者刚刚学完动态规划会走火入魔到看到最优化问题就想用动态规划来求解,嗯,那么这样应该也是可以的吧……不,等等,这个问题不是简单做一个排序或者做一个遍历就可以求解出来了吗?所以学完动态规划后,你一定要注意,遇到这些简单的问题不要把事情变得更复杂了。**先考虑一下能不能通过排序来简化问题,如果不能,才极有可能是动态规划问题。**还是看个例子。

最小的 k 个数

问题:输入整数数组 arr ,找出其中最小的 k 个数。例如,输入 4、5、1、6、2、7、3、8 这 8 个数字,则最小的 4 个数字是 1、2、3、4。示例1:输入:arr = \[3,2,1\], k = 2输出:\[1,2\] 或者 \[2,1\]示例2:输入:arr = \[0,1,2,1\], k = 1输出:\[0\]

我们发现虽然这个问题也是求“最”值,但其实只要通过排序就能解决,所以我们应该用排序、堆等算法或者数据结构来解决,而不应该用动态规划。

数据不可交换(Non-swapable)

还有一类问题,可以归类到我们总结的几类问题里去,但是不存在动态规划要求的重叠子问题(比如经典的八皇后问题),那么这类问题就无法通过动态规划求解。这种情况需要避免被套进去。

全排列

问题:给定一个没有重复数字的序列,返回其所有可能的全排列。示例:输入: \[1,2,3\]输出:\[\[1,2,3\],\[1,3,2\],\[2,1,3\],\[2,3,1\],\[3,1,2\],\[3,2,1\]\]

这个问题虽然是求组合,但没有重叠子问题,更不存在最优化的要求,因此可以使用回溯处理,并不是动态规划的用武之地。

总结

今天,我们一起探讨了动态规划问题的本质,更准确或更加严谨地说,动态规划是一种指导我们解决问题的思想。

接着我们列出了辨别一个算法问题是否该使用动态规划来解的五大特点:

如果面试题目出现这些特征,那么在 90% 的情况下你都能断言它就是一个动归问题。

当然了,就像我前面所讲的,你还需要考虑这个问题是否包含重叠子问题与最优子结构,在这个基础之上你就可以 99% 断言它是否为动归问题,并且也顺势找到了大致的解题思路,我会在后面的课程中继续跟你探讨这些问题,彻底解决你的疑惑。

通过上述这几个鲜明的特点,相信你能够在将来迅速地判断出问题是否为动态规划类问题,并使用对应的思想和套路来应对算法或面试问题。

所以,什么样的问题应该使用动态规划?
求最值/求可行性/求方案总数+重叠子问题+最优子结构

05|面试即正义第一期:什么样的问题应该使用动态规划?相关推荐

  1. 2022.05面试总结

    前言 今年四月份正在办公室高高兴兴的写bug,突然就被通知毕业了,传说中的天选打工人!于是开始了五月份找工作的苦逼生活.总体上来说市场比较冷淡,前后面试了20多家,只拿到了4个offer ,整体涨幅在 ...

  2. [Leetcode][程序员面试金典][面试题16.11][JAVA][跳水板][数学][动态规划]

    [问题描述][简单] [解答思路] 边界问题 k=0 ,不能产生跳水板,返回空数组 shorter 等于longer,只有一种跳水板,返回longerk 思路 一般情况,k块木板,k种可能 跳水板的长 ...

  3. 英语面试对话场景[进入外企的敲门砖]

    本文转自: http://blog.csdn.net/tryonmind/article/details/1626004 I:Interviewer(面试者) A:Applicant(求职者) 教育背 ...

  4. 有三AI模拟面试服务上线,一对一服务助你求职

    文/编辑 | 言有三 各位粉丝朋友们,今天正式上线有三AI模拟面试服务,在这里会给大家提供有偿的模拟面试,针对简历写作,项目技能,求职方向等做相关辅导. 模拟面试服务内容 要找到一份好工作可以是一个非 ...

  5. verilog异步复位jk触发器_以不变应万变的异步FIFO面试宝典(二)

    异步FIFO面试宝典(二) 上一期为童鞋们带来了FIFO工作的基本原理,本期将继续与各位童鞋探讨FIFO类面试相关问题.首先让我们回顾一下上一期的课后思考题: 如果读时钟域速度较快.写时钟域速度较慢( ...

  6. 如何搞定不同公司的算法面试?(早早聊分享文字版)

    前几天西法参加了<前端早早聊>第 24 界的分享.我的分享主题是<如何搞定不同公司的算法面试?> 这是这次分享的文字版,供大家查看.如果大家需要分享的原版 ppt,也可以到我的 ...

  7. 蓬莱小课:数据分析岗位求职,面试阶段应该怎么应对

    请用几个词描述你收到面试通知后的心情 激动!兴奋!成就感满满! 当然还有,慌-- 因为面试环节是对候选人综合素质的考察 你的思维能力.表达能力 甚至一言一行都是面试官考察的范围 那么,进入到了面试阶段 ...

  8. 面试神仙打架?探秘华为人工智能工程师岗位

    ©原创作者 | 三金 写在前面: "得人工智能者得天下",这句话在互联网圈子流传盛广.人工智能工程师岗位薪酬普遍较高,让部分毕业生眼前一亮,毕业后直接加入到了相关岗位的竞争中,面试 ...

  9. 阿里JAVA实习生面试总结(2019年春招)

    开篇 首先说说结果.前三面通过,最后HR面挂掉了.一路走来虽然跌跌撞撞,但是收获颇多.写一篇博客记录一下自己的阿里面试之路. 提一下背景,双非渣本大三,大一大二学C/C++,大三开始学Java,有参加 ...

最新文章

  1. easyui日期处理(开始时间和结束时间)
  2. Uva1593 代码对齐
  3. Overlay 网络 — Overview
  4. [LeetCode] 142. Linked List Cycle II
  5. 计算机二级html真题,计算机二级《Web程序设计》试题及答案
  6. dell服务器sd卡装系统,DELL服务器通过sd卡安装系统(iDRACUsevFlash).doc
  7. httpclient 设置超时时间_面试官:技术选型,HttpClient还是OkHttp?
  8. unity3d软阴影和硬阴影的原理_Unity3D中两种默认阴影的实现
  9. 基于.NetCore3.1搭建项目系列 —— 使用Swagger导出文档 (番外篇)
  10. kubernetes系列12—二个特色的存储卷configmap和secret
  11. xargs命令_Linux xargs命令:一个给其他命令传递参数的过滤器
  12. oracle人才盘点演示,Oracle 11g Golden Gate DDL单向同步实例演示
  13. 手机端与pc端页面html5,浅谈pc和移动端的响应式的使用
  14. 闲话WPF之二十(WPF中的传递事件 [2] )
  15. 《实用软件工程》课程教学大纲(Practicality Software Engineering)
  16. ICH E2B | ICSR 电子传输网关对接解决方案(CDE EDI)
  17. 智能药盒 树莓派与Arduino实现药板自助取药
  18. Plotly基础教程
  19. 03-12306验证码文字 识别
  20. amd cpu排行_amd cpu性能排行图_笔记本CPU性能排行天梯图-2012最新笔记本CPU天梯图...

热门文章

  1. @kubernetes(k8s)的kubectl的使用及资源类型pod生命周期与资源清单详解
  2. ingress?另一种
  3. springboot 给图片加二维码
  4. Mastering Atari, Go, Chess and Shogi by Planning with a Learned Model (MuZero)
  5. stm32成长记之滴答时钟
  6. mysql 1067 datadir_MySQL修改datadir后启动服务发生1067错误的解决办法
  7. Shell脚本开发实战--工具篇
  8. 互联网根服务器体积位置,【群晖 DS418play 4盘位NAS网络存储服务器使用总结】功耗|构架|盘位|噪音|系统_摘要频道_什么值得买...
  9. slashdot网站架构:硬件和软件 zz 1
  10. 杰理之播放电话来电号码声音,前面播放“嘟嘟”声【篇】