最近在刷Coursera的算法课编程作业,发现两年不碰coding实在是忘得差不多了。做到dp题不得不翻翻书了,发现原来动态规划的基础就没怎么打好,打算写一些dp的题解。本文主要介绍简单的动态规划问题,用于笔者自己整理思路。
从01背包开始:
用一定的总重量,装最贵的物品。因为每个物品只有装与不装两种策略,故称0/1背包。上题中,单个物品的质量和价值相等(因为是金条,很合理)。
考虑一个朴素的深搜算法,复杂度是O(
void
穷举每个物品放与不放的情况,用当前重量剪枝,最后会形成一棵二叉树。非常地朴素,当然也非常地慢。
反过来考虑的话,用递推,对最后一个物品拿不拿进行分类。可以得到以下的递推式:
这方便我们写出一个递归算法:
int
这个算法是逆推的,和dfs一样也是指数复杂度。
由这个递推方程,或者现在应该叫状态转移方程了,不难看出,“考虑第i个元素和余下wei重量时”的最优解只需知道“考虑第i-1个元素和余下wei重量时”和“考虑第i-1个元素和余下wei-w[i]重量时”的最优解。从动态规划的原理上来讲,这满足最优化原理(最优子结构)和无后效性。从算法实现的角度,它给了我们一个遍历数组的顺序。
先开一个二维数组dp[i][wei],那么根据状态转移方程,任何一个位置的值都可以由它上方和左上方位置的值确定,并且这个过程是递归的。
那么我们自然确定,以先从左往右,再从上往下的顺序遍历数组。
再预处理一下边界条件
就可以愉快地dp啦
for
什么?两层循环?这也太好写了吧。比前面搜索,递归不知道高到哪里去了。最重要的是只要
但是呢,聪明的小伙伴已经发现啦,本题调用了一个二维数组,用了
总结,动态规划算法本质在于寻找合法的状态转移方程,这一步往往是困难的。带什么参数dp?如何构造良好的遍历顺序?从个人经验来看,从深搜或者递归向动态规划优化可能是一种比较自然的方法。