【答题技巧】动态规划的4步解题法

自从动态规划出现在算法面试后,就成为求职者绕不开的“噩梦”。

动态规划的一大难点就是题型众多而又没有统一的解法。想要熟练掌握往往需要大量刷题来练习,然而,大多数人连题目都解不出,就更别说是掌握一定的解题规律了。

我曾获得全国算法竞赛金牌,参加过ACM全球总决赛。针对动态规划,我总结了一套“4步解题法”。今天通过一道经典题,我来讲讲如何用4步解题法搞定动态规划题型。

换硬币问题

你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多。买一本书需要27元。如何用最少的硬币组合正好付清,不需要对方找钱?

第一步:确定状态

状态在动态规划中的作用属于定海神针。解动态规划时需要开一个数组,这里的“状态”就是指数组的每个元素 f[i]或f[i][j] 代表什么。 确定状态需要两个意识:最后一步和子问题

1.最后一步

这道题中,我们不知道最优策略是什么,但最优策略肯定是K枚硬币a1,a2……aK面值加起来是27。这里的“最后一步”就是存在最后一枚硬币aK。

除去aK,前面的硬币面值和为27-aK。
在这里插入图片描述
这里有两个关键点:

① 我们不关心前面的K-1枚硬币是怎么拼出27-aK的,也不知道aK和K,但是可以确定前面的硬币拼出了27-aK。

② 因为是最优策略,所以拼出的27-ak硬币数一定要最少(否则就不是最优策略)

2.子问题

现在问题变成了:最少用多少枚硬币可以拼出27-aK。也就是将原问题(27)转化成了一个子问题,而且规模更小(27-aK)。这种与原问题内核一致,但是规模更小的问题,就叫子问题。

为了简化定义,我们设状态f(X)=最少用多少枚硬币拼出X。所以问题就从求f(X)变成求f(X-aK) 我们目前还不知道最后的硬币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的硬币)

除此以外,没有其他的可能了。因为要求最少的硬币数,所以问题的解就可以这样表示:
f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}

第二步:转移方程

设状态f[X]=最少用多少枚硬币拼出X对于任意X,f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}
在这里插入图片描述
实际面试中,如果正确列出转移方程,问题基本就解决一半了。 很多同学基本也可以做到写出状态转移方程,但真正写程序的时候往往会出现很多错误或问题。 这就涉及到在在代码前的两个重要步骤,就是我们4步解题法的第三步和第四步。

第三步:初始条件和边界情况

f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}的边界情况是X-2, X-5或者X-7不能小于0(硬币面值为正)

故对边界情况设定如下:

如果硬币面值不能组合出Y,就定义f[Y]=正无穷例如f[-1]=f[-2]=…=正无穷;f[1] =min{f[-1]+1,f[-4]+1,f[-6]+1}=正无穷,表示拼不出1

特殊情况:本题的F[0]对应的情况为F[-2]、F[-5]、F[-7],按照上文的边界情况设定结果是正无穷。

但是实际上F[0]的结果是存在的(即使用0个硬币的情况下),F[0]=0。 这种用转移方程无法计算,但是又实际存在的情况,就必须通过手动定义。

所以这里定义初始条件为: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

按照以上4步套路,基本上可以解决绝大多数类型的动态规划题。另外我在《国内大厂高频动规题详解》分析了近3年国内大厂90%的高频动规笔面试题,可以帮你搞定7大动态规划题型。

最后总结

根据这道题,我总结了动态规划4步解题法

  • 确定状态
  • 研究最优策略的最后一步
  • 转化为子问题
  • 转移方程
  • 根据子问题定义直接得到
  • 初始条件和边界情况
  • 细心,考虑周全
  • 计算顺序
  • 利用之前的计算结果

按照以上4步套路,基本上可以解决绝大多数类型的动态规划题。

**此外,还有同学反馈DP的另一大难点是无法判断遇到的题目是否能用动规解法。**在这里,我也总结了可以使用动态规划的题目的提问方式:

求最大值/最小值

  • 从左上角到右上角路径的最大数字和
  • 最长上升子序列长度

求方案数

  • 有多少种方式走到右下角
  • 有多少种方法选出K个数使得和是Sum

除了最值型动态规划,想要了解4步法在更多类型动态规划中的运用,可以来听《动态规划专题班》

如果你碰到一个问题是以上提问方式,那么有90%的概率是使用动态规划来求解。要重点说明的是,若问题是让你求出“所有的”方案和结果,则肯定不是使用动态规划

最后,分享我精心整理的动态规划经典题型,领悟了“4步解题法”后,不妨来考考自己,试着做一下这些题:

easy级别

最小路径和

爬楼梯

丢鸡蛋

最长上升连续子序列

栅栏染色

medium级别

最大正方形

石子归并

完美平方

猜数游戏II

杆子分割

hard级别

吹气球

俄罗斯套娃信封

布尔括号

樱桃采集

奇怪的打印机

Super hard级别

最优账户结余

序列计数

段式石子归并

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值