Nginx 如何支持 HTTPS,大厂 Java 高级多套面试专题整理集合
4 步套路,解决动态规划问题
1、确定问题状态
提炼最后一步
的问题转化
2、转移方程,把问题方程化 3、按照实际逻辑设置初始条件和边界情况 4、确定计算顺序并求解
结合实例感受下:
你有三种硬币,分别面值 2 元,5 元和 7 元,每种硬币都有足够多。买一本书需要 27 元。如何用最少的硬币组合正好付清,不需要对方找钱?
关键词“用最小的硬币组合正好付清”——“最小的组合”,求最值问题,动态规划。
**正常人第一反应思路:**最少硬币组合?优先使用大面值硬币——7+7+7+5=26 额?可求解目标是 27 啊……改算法——7+7+7+2+2+2=27,总共用了 6 枚硬币正好 27 元.实际正确答案:7+5+5+5+5=27,才用了 5 枚硬币。所以这里贪心算法是不正确的。
套路用起来:
第一步,确定问题状态。
动态规划问题求解需要先开一个数组,并确定数组的每个元素 f[i]代表什么,就是确定这个问题的状态。类似于解数学题中,设定 X,Y,Z 代表什么。
A、确定状态首先提取【最后一步】
最优策略必定是 K 枚硬币 a1, a2,…, aK 面值加起来是 27。
找出不影响最优策略的最后一个独立角色,这道问题中,那枚最后的硬币“aK”就是最后一步。把 aK 提取出来,硬币 aK 之前的所有硬币面值加总是 27- aK 因为总体求最硬币数量最小策略,所以拼出 27- aK 的硬币数也一定最少(重要设定)。
B、**转化子问题。**最后一步 aK 提出来之后,我们只要求出“最少用多少枚硬币可以拼出 27- aK”就可以了。
这种与原问题内核一致,但是规模变小的问题,叫做子问题。
为简化定义,我们设状态 f(X)=最少用多少枚硬币拼出总面值 X。我们目前还不知道最后的硬币 aK 面额多少,但它的面额一定只可能是 2/5/7 之一。如果 aK 是 2,f(27)应该是 f(27-2) + 1 (加上最后这一枚面值 2 的硬币)如果 aK 是 5,f(27)应该是 f(27-5) + 1 (加上最后这一枚面值 5 的硬币)如果 aK 是 7,f(27)应该是 f(27-7) + 1 (加上最后这一枚面值 7 的硬币)除此以外,没有其他的可能了。
至此,通过找到原问题最后一步,并将其转化为子问题。为求面值总额 27 的最小的硬币组合数的状态就形成了,用以下函数表示:
f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}
第二步,转移方程,把问题方程化。
f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}(动态规划都是要开数组,所以这里改用方括号表示)
实际面试中求解动态规划类问题,正确列出转移方程正确基本上就解决一半了。
但是请问:这与递归有什么不同??
递归的解法:
执行图如下:
要算 f(27),就要递归 f(25)、f(22)、f(20),然后下边依次递归……(三角形表示)。
问题明显——重复递归太多。
这是求 f(27),还可以勉强递归。如果求 f(100)呢?简直是天文数字。最终结果就是递归超市。
求总体最值,一定优先考虑动态规划不要憨憨的去递归。
插入一下~
需要掌握的动态规划面试解题技巧还包括坐标型、位操型、序列型、博弈型、背包型、双序列以及一些高难面试题解。
本文篇幅有限无法逐一讲清,大家来白嫖我的在线分享吧(纯干货)。
第三步,按照实际逻辑设置边界情况和初始条件。
**【必做】**否则即使转移方程正确也大概率无法跑通代码。
f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}的边界情况是[x-2]/[x-5]/[x-7]不能小于 0(硬币面值为正),也不能高于 27。
故对边界情况设定如下:
如果硬币面值不能组合出 Y,就定义 f[Y]=正无穷例如 f[-1]=f[-2]=…=正无穷;f[1] =min{f[-1]+1, f[-4]+1,f[-6]+1}=正无穷,
**特殊情况:**本题的 F[0]对应的情况为 F[-2]、F[-5]、F[-7],按照上文的边界情况设定结果是正无穷。
但是实际上 F[0]的结果是存在的(即使用 0 个硬币的情况下),F[0]=0。可是按照我们刚刚的设定,F[0]=F[0-2]+1= F[-2]+1=正无穷。
岂不是矛盾?
这种用转移方程无法计算,但是又实际存在的情况,就必须通过手动定义。
这里手动强制定义初始条件为:F[0]=0.
而从 0 之后的数值是没矛盾的,比如 F[1]= F[1-2]+1= F[-1]+1=正无穷(正无穷加任何数结果还是正无穷);F[2]= F[2-2]+1= F[0]+1=1……
第四步,确定计算顺序并计算求解
那么开始计算时,是从 F[1]、F[2]开始呢?还是从 F[27]、F[26]开始呢?
判断计算顺序正确与否的原则是:当我们要计算 F[X](等式左边,如 F[10])的时候,等式右边(f[X-2], f[X-5], f[X-7]等)都是已经得到结果的状态,这个计算顺序就是 OK 的。
实际就是从小到大的计算方式(偶有例外的情况我们后边再讲)。
例如我们算到 F[12]的时候,发现 F[11]、F[10]、F[9]都已经算过了,这种算法就是对的;而开始算 F[27]的时候,发现 F[26]还没有算,这样的顺序就是错的。
很显然这样的情况下写一个 FOR 循环就够了。
回到这道题,采用动态规划的算法,每一步只尝试三种硬币,一共进行了 27 步。算法时间复杂度(即需要进行的步数)为 27*3。
与递归相比,没有任何重复计算。
**原题练习及实际代码:**这道题是 lintcode 编号 669 的 Coin Change 问题。代码如下:
最后总结:
1、这是求最值问题,用动态规划方式求解。2、进入求解过程,先确定问题状态
提炼最后一步(最优策略中使用的最后一枚硬币 aK)-子问题转化 (最少的硬币拼出更小的面值 27-aK)3、构建转移方程 f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}(求 min 是因为题目要求求最小)4、设置初始条件和边界情况 f[0] = 0, 如果不能拼出 Y,f[Y]=正无穷 5、确定计算顺序并计算求解 f[0], f[1], f[2]……
实际上按照以上 4 步套路,基本上可以应对绝对大多数的动态规划面试题。
最后的内容
在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个 40k,没做准备只有 30k+,你们懂那种感觉吗)
如何准备面试?
1、前期铺垫(技术沉积)
程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。
下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习 Java,应对大厂面试官的灵魂追问
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】
这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!
Java 程序员必看《Java 开发核心笔记(华山版)》
Redis 学习笔记
Java 并发编程学习笔记
四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇
Java 程序员必看书籍《深入理解 ava 虚拟机第 3 版》(pdf 版)
大厂面试必问——数据结构与算法汇集笔记
其他像 Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s 等等我都整理好,这里就不一一展示了。
2、狂刷面试题
技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。
①大厂高频 45 道笔试题(智商题)
②BAT 大厂面试总结(部分内容截图)
③面试总结
3、结合实际,修改简历
程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:
以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。
另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
评论