算法概论笔记 - 动态规划法

定义一组子问题,按照由小到大,以小问题的解答支持大问题求解的模式,依次解决所有的子问题,并最终得到原问题的解答。

隐含思想

DAG有向无圈图的拓扑排序

节点对应于我们定义的子问题,边表示子问题间的依赖关系:即如果求解子问题B必须依赖子问题A的解答,则(概念上)存在一条由A到B的边。

子问题性质
存在子问题间的一种排序以及如下的关联关系:对于任意一个 子问题,这种关联关系说明了如何在给定(在排序中)相对其“较小”的子问题的解的前提下,求得该子问题的解

妙处
恰当地选择子问题,使得所有重要的信息都得以保存并便于今后使用

常见子问题模式
模式1

输入为x1, x2, ..., xn,子问题为x1, x2, ..., xi。

最终子问题数量将为线性。

模式2

输入为x1, x2, ..., xn和y1, y2, ..., ym,子问题为x1, x2, ..., xi和y1, y2, ..., yj。

最终子问题数量为O(m*n)

模式3

输入为x1, x2, ..., xn,子问题为xi, x(i+1), ..., xj。

最终子问题数量为O(n^2)。

模式4

输入为树,子问题为其子树。

若树有n个节点,则最终子问题数量为O(nlogn)。

最长递增子序列

输入:一个数字序列a1、a2、...、an。

输出:从以上序列中按顺序选出的一个子集,并且严格单调递增,形如a(i1)、a(i2)、a(ik),其中1<=i1<i2<...<ik<=n。

策略1
  1. 为每个元素建立一个对应的节点,对任意两个存在递进关系的元素ai和ak(即同时满足i<j且ai<ak),增加一条连接两者对应节点的有向边(i, j)

  2. 形成dag,求递增子序列问题变为寻找dag中的最长路径

for j = 1,2,...,n:
    L(j) = 1 + max{L(i):(i, j) in E}
return max{L(j)}

L(j)是以序列中j为终点的最长路径,即对应于最长递增子序列的长度

实现

  1. 为方便图的邻接表表示,可变形为
for i = 1,2,...,n:
    for (i, j) in E:
    L(j) = max {L(j), L(i) + 1}
return max{L(j)}

see implement: dp.LongestIncreSub

  1. 求反转图G的邻接表表示

此时无需构造原图G

时间复杂度max{O(n^2), O(V+E)}

策略2

子问题:将LCS(i)记做包含

的最长递增子序列长度。

LIS[i] = max{1,LIS[k]+1},其中,对于任意的k<=i-1,arr[i] > arr[k]。

最长公共子序列LCS

输入:两个序列X![](http://latex.codecogs.com/svg.latex?(x_0, x_1, x_2, ..., x_m)),Y![](http://latex.codecogs.com/svg.latex?(y_0, y_1, y_2, ..., y_n))。

输出:一个序列S任意删除若干个字符得到新序列T,则T叫做S的子序列。两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列。

策略
子问题:将LCS(i, j)记做![](http://latex.codecogs.com/svg.latex?(x_0, x_1, x_2, ..., x_i))与![](http://latex.codecogs.com/svg.latex?(y_0, y_1, y_2, ..., y_j))的最长公共子序列长度。

考察

,若![](http://latex.codecogs.com/svg.latex?x_i = y_j),则![](http://latex.codecogs.com/svg.latex?LCS(i, j) = LCS(i-1, j-1)+1)。否则![](http://latex.codecogs.com/svg.latex?LCS(i, j) = max {LCS(i-1, j), LCS(i, j-1)})


![](http://latex.codecogs.com/svg.latex?LCS(i, j)=\begin{cases}0 & if ; i=0 ; or ; j=0\LCS(i-1, j-1)+1 & if ; i>0, j>0, and ; x_i = x_j\max {LCS(i-1, j), LCS(i, j-1)} & if ; i>0, j>0, and x_i != x_j\end{cases})

时间复杂度max{O(n^2)}

变体1:最长公共子串

子序列不要求子序列中的字符在原序列中连续,而子串要求连续。

策略
子问题:![](http://latex.codecogs.com/svg.latex?L(i, j))表示以

为结尾的相同子串的最大长度.

考察

,若![](http://latex.codecogs.com/svg.latex?x_i = y_j),则![](http://latex.codecogs.com/svg.latex?L(i, j) = L(i-1, j-1)+1)。否则![](http://latex.codecogs.com/svg.latex?L(i, j) = 0)


![](http://latex.codecogs.com/svg.latex?L(i, j)=\begin{cases}0 & if;i=0;or;j=0\L(i-1, j-1)+1 & if;i>0, j>0, and ; x_i = x_j\0 & if ; i>0, j>0, and x_i != x_j\end{cases})

在上述过程中记录![](http://latex.codecogs.com/svg.latex?L(i, j))的最大值,最后即可得到最长公共子串的长度

时间复杂度O(n^2)

最长公共子串类似:最大子串和

PAT1007. Maximum Subsequence Sum

输入:给定一个序列X![](http://latex.codecogs.com/svg.latex?(x_0, x_1, x_2, ..., x_m))。

输出:找出该序列具有最大和的子串。如果该序列所有数均为负数,则输出0。

策略
子问题:

表示以
为结尾的子串的最大和。

考察

,若
,则
对于
毫无帮助,即![](http://latex.codecogs.com/svg.latex?Sum(i) = x_i),否则![](http://latex.codecogs.com/svg.latex?Sum(i) = x_i + Sum(i-1))


![](http://latex.codecogs.com/svg.latex?Sum(i)=\begin{cases}x_i & if ; i=0 ; or ; Sum(i-1)<=0\x_i + Sum(i-1) & if ; Sum(i-1)>0\end{cases})

在上述过程中记录

的最大值,并根据第一种情况重新修改tempLeft(当前子串左边开始位置)。最后即可得到最大子串和以及最大子串。

时间复杂度O(n)

空间复杂度O(1)

算法过程中只需存储上一步的Sum和位置信息

变体2:最长递增子序列LIS
  1. 对输入序列X进行排序,得到序列Y
  2. 求解序列X和Y的最长公共子序列,即是序列X的最长递增子序列

时间复杂度O(n^2)

编辑距离

随意插入空隙“-”到每个字符串中使两个字符串对齐,对于该种对齐方式,代价为两个字符串对应字母不相同的列数。如:

S - N O W Y
S U N N - Y

SNOWY和SUNNY该种对齐方式的代价为3

编辑距离是指两个字符串的各种对齐所可能具有的最小代价。

策略
子问题:定义E(i, j)为寻找两个字符串x[1...i]与y[1...j]之间的编辑距离。
由于要将E(i, j)表示为较之更小的子问题的表达式,我们考虑x[1...i]与y[1...j]所有对齐中最右侧的列的可能情况:

  1. x[i]与-
  2. -与y[j]
  3. x[i]与y[j]
    因此,E(i, j) = min{1+E(i-1, j), 1+E(i, j-1), diff(x[i], y[j])+E(i-1, j-1)}

当x[i]=y[j]时,diff()取0;否则取1

所有子问题E(i, j)的解构成一个二维表,要保证求解顺序(E(i-1, j), E(i, j-1)和E(i-1, j-1)总是在E(i, j)之前求解)

算法如下:

for i = 0, 1, 2 ... m:
    E(i, 0) = i
for j = 1, 2 ... n:
    E(0, j) = j
for i = 1, 2 ... m:
    for j = 1, 2 ... n:
        E(i, j) = min{E(i-1, j)+1, E(i, j-1)+1, E(i-1, j-1)+diff(i,j)}
return E(m, n)

see implement: dp.EditingDistance
时间复杂度为O(m*n)

扩展
在二维表中,令{(i-1, j-1)->(i, j) : x[i]=y[j]}中边的长度置为0,并将其他所有边的长度置为1。则求编辑距离问题变为求dag中的最短路径,即点{0,0}与{m,n}之间的最短路径。

在该路径上,向下的移动表示删除,向右为插入,而对角线移动表示匹配或者替换。

背包问题

一共有n件物品,分别重w1,w2, ..., wn,价值v1,v2,...,vn。背包最多能装W磅,如何组合才能使背包携带的价值最高。

广泛应用于需要机遇有限资源进行选择判断的领域

多副本的背包问题

每种物品的数量无限
1. 子问题:考虑容量较小的背包
定义K(w) = 容量w可以容纳的最高价值
若K(w)的最优解包含物品i,将物品i去掉,则会留下K(w-wi)的最优解。不知道包含哪些i,因此尝试所有的可能
![](http://latex.codecogs.com/svg.latex?K(w) = max_{i:w_i<=w} {K(w-w_i)+v_i})

K(0) = 0
for w = 1 to W:
    K(w)= max{K(w-w(i))+v(i):wi<=w}
return K(W)

时间复杂度O(nW)

see implement: dp.bag.MultiBagProWithCapa

构造DAG
针对每个w构造节点,其中w in 0~W。对于每个节点与每个物品,使用该物品的容量确定边的终点,使用该物品的价值确定边的权值,可以构造出一个DAG。求背包所能容纳的最高价值最终归结为寻找该DAG的最长路径。

2. 子问题:考虑较少的物品种类
TODO

单副本的背包问题

每种物品都只有一件
策略
在K(w)子问题中包含关于哪件物品已经被使用过的信息。
定义子问题K(w, j) = 基于背包容量w和物品1, ..., j所能得到的最高价值
用更小的子问题表示K(w, j):
![](http://latex.codecogs.com/svg.latex?K(w, j) = max(K(w-w_j,j-1)+v_j, K(w, j-1)))

initialize all K(0, j) = 0 and all K(w, 0) = 0
for j = 1 to n:
    for w = 1 to W:
        if w(j) > w: K(w, j) = K(w, j-1)
        else: K(w, j) = max{K(w, j-1), K(w-w(j), j-1)+v(j)}

时间复杂度O(nW)

see implement: dp.bag.SingleBagPro

矩阵链式相乘

一个mn的矩阵与一个np的矩阵相乘,约需要进行mnp次乘法。通过在矩阵链式相乘公式的不同位置插入括弧,我们可以得到很多不同的计算方式,那么哪种乘法次序代价最小?
自然的表示
满二叉树:每个矩阵对应一个叶节点,树根为最终的乘积,而树的内部节点表示中间过程的部分乘积。各种可能的乘法次序对应不同的满二叉树。

如果一个树最优,那么其子树必然也最优

策略
假设我们需要计算

其中每个矩阵的维数分别为![](http://latex.codecogs.com/svg.latex?m_0m_1, m_1m_2, ..., m_{n-1}m_n)
定义![](http://latex.codecogs.com/svg.latex?C(i, j) = the ; minimum ; cost ; of ; computing A_i
A_{i+1}...A_j)
C(i, j)可分割为C(i, k)和C(k+1, j)两个子问题,即
![](http://latex.codecogs.com/svg.latex?C(i, j) = min_{i<=k<j}[C(i,k) + C(k+1, j) + m_{i-1}m_km_j]​)

由于C(i, k)的维数为

,C(k+1, j)的维数为

求解顺序:当求解到某个子问题时,比该子问题规模小的子问题已被求解。

因为(i,j)坐标并不在(i+1, j)坐标的下边或右边,此时无法按照从上至下,从左至右的求解顺序

for i = 1 to n: C(i, i) = 0
for s = 1 to n-1:
    for i = 1 to n-s:
        j = i+s
        C(i, j) = min{C(i,k) + C(k+1, j) + m(i-1)*m(k)*m(j):i<=k<j}
return C(1, n)

see implement: dp.MatrixChain

时间复杂度O(n^3)

动态规划 VS 递归记忆

记忆:递归算法记住之前的调用结果

  • 优点:记忆只会求解那些实际被用到的子问题
  • 缺点:递归所具有的的间接开销通常较高,其大O符号所包含的常数因子相对而言较大
写在最后
  • 立个Flag,TODO will be done some day。
  • 渣代码,且轻喷​:worried:​。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 回溯算法 回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并...
    fredal阅读 13,508评论 0 89
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,187评论 0 18
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,660评论 0 33
  • 分治方法 将问题划分成互不相交的子问题 递归地求解子问题 将子问题的解组合起来 动态规划(两个要素:最优子结构、子...
    superlj666阅读 474评论 0 0
  • 原文在这里 http://blog.csdn.net/dq_dm/article/details/45043689...
    superczb阅读 1,058评论 0 1