在算法竞赛中,动态规划的重要性不言而喻。它是所有算法中可以说是最难理解的,也是最难提高的。它范围甚广,变换万千。
所有的关于动态规划的讲解都只是其中的冰山一角,当然,这篇以及后面我要更新的也是。不过在动态规划的世界里,很多人为此沉迷了很多时间,同时也得到了很多理解和感悟。
除了最基础的数塔问题,我们进一步需要学习的分别是DAG、LIS、LCS、LCIS。下面分别进行讲解
DAG
也叫有向无环图,是学习动态规划的基础。很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。
根据紫书上面的总结,DAG的最长路和最短路都可以用记忆化搜索和递推两种实现方式。
由于DAG最长(短)路的特殊性,有两种“对称”的状态定义方式:
状态1: 设d[i]为从i出发的最长路,则d[i]=max(d[j]+1|(i,j)∈E)
状态2: 设d[i]为从i结束的最长路,则d[i]=max(d[j]+1|(i,j)∈E)
多阶段决策问题
多段图是一个特殊的DAG,其结点可以划分成若干个阶段,每个阶段只由上一个阶段所决定。它的最优化问题往往可以用动态规划解决,其中,状态及其转移类似于回溯法中的解答树。解答树中的“层数”,也就是递归函数中的“当前填充位置”cur,描述的是即将完成的决策序号,在动态规划中被称为阶段。
设d[i][j]为到达第i行第j列的最小(最大)花费
每做一次决策就可以得到解得一部分,当所有决策做完之后,完整的解就“浮出水面”了。
LIS
是最长上升子序列的缩写。给定n个整数A1,A2,…,An,按从左到右的顺序选出尽量多的整数,组成一个上升的子序列(子序列可以理解为:删除0个或者多个数,其他数的顺序不变),例如序列:1,6,2,3,7,5,可以选出上升子序列1,2,3,5,也可以选出1,6,7,但前者更长。注意,选出的上升子序列中相邻元素不能相等。
设d[i]为以i结尾的最长上升子序列的长度
可以得到状态转移方程:
d[i]=max(0,d[j]|j
最后遍历一次,答案为
max(d[i])
如果LIS中的元素可以相等,把小于号改成小于等于号即可。
LCS
全称叫做最长公共子序列问题。给定两个子序列A和B,求长度最大的公共子序列。
设d[i][j]为A1,A2,...,Ai和B1,B2,...Bj的LCS长度
则分成两种情况来讨论:
(1)a[i] == b[j]时 d[i][j] = d[i-1][j-1]+1;
(2)a[i] != b[j]时 d[i][j] = max(d[i-1][j],d[i][j-1]);
LCIS
是增加的一个内容,叫做最长递增公共子序列。
设d[i][j]为以a串的前i项和b串的前j项且以b[j]为结尾构成的LCIS的长度
同时也可以分成两种情况来讨论:
(1)a[i] == b[j]时 d[i][j] = max+1;
(2)a[i] > b[j]时 max = max(max,d[i-1][j]);
如果想要更简单一点,可以在维度上进行优化:
(1)a[i] == b[j]时 d[j] max+1;
(2)a[i] > b[j]时 max = max(max,d[j]+1);
推荐题目:
UVa10635
UVa11584
UVa10534
UVa11404
UVa116
UVa10891
hdu1234
hdu1421
hdu1257
hdu1159
hdu5904
poj1088
poj2247
poj1159
题解有时间就会来补充。