动态规划是常考算法,将指数级的问题降到多项式级别。
动态规划和分治法都是将问题划分成子问题进行求解,它们的区别主要是:
- 分治法的子问题无重叠
- 动态规划的子问题有重叠,并且重叠的个数是指数级别的
动态规划和贪心法的相同之处是原问题包含子问题的最优解,而它们的区别在于:
- 贪心法只看局部最优,最终达到全局最优
- 对于动态规划局部最优不一定是全局最优
1. Weighted interval scheduling
带权重的最大兼容子集,不能用贪心法进行求解,需要考虑动态规划的解法。首先将活动按照结束时间排序。两种情况,一种是选当前的j,那么最优解等于从后往前找第一个与j不冲突的活动(使用二分查找加快速度)的opt加上j的权重,一种是不选j,那么最优解等于j的前一个活动的最优解。如下式:
o p t ( j ) = m a x { v j + o p t ( p ( j ) ) , o p t ( j − 1 ) } opt(j)=max\{v_j+opt(p(j)),opt(j-1)\} opt(j)=max{
vj+opt(p(j)),opt(j−1)}
ps.若按照开始时间排序,要反过来找,那么opt(i)可以定义为,i~n个活动,p(i)是从前往后找第一个不冲突的活动。opt(n+1)=0,return opt(1)。
o p t ( i ) = m a x { v i + o p t ( p ( i ) ) , o p t ( i + 1 ) } opt(i)=max\{v_i+opt(p(i)),opt(i+1)\} opt(i)=max{
vi+opt(p(i)),opt(i+1)}
2. Segmented least squares
线段拟合点集问题,希望线段尽量少,error尽量小。
error定义为E+cL ,E是平方误差之和,L是线段条数。
opt(j) 是 p 1 . . . p j p_1...p_j p1...pj的最小error,e(i,j)是 p i , p i + 1 . . . p j p_i,p_{i+1}...p_j pi,pi+1...pj的最小平方误差
o p t ( j ) = min 1 ≤ i ≤ j { e i j + c + o p t ( i − 1 ) } opt(j)=\min \limits_{1\leq i\leq j}\{e_{ij}+c+opt(i-1)\} opt(j)=1≤i≤jmin{
eij+c+opt(i−1)}
这个算法的瓶颈在于计算 e i j e_{ij} eij,对j要扫一遍,j固定了,i要扫一遍,i固定了,在i j之间要扫一遍, 使得算法的效率是 O ( n 3 ) O(n^3) O(n3).
3. 买卖股票系列
- 只能买卖一次
Best Time to Buy and Sell a Stock
f(j):第j次卖出的最优解, f(1)=0, return max 1 ≤ j ≤ n ( f ( j ) ) \max\limits_{1\leq j\leq n}(f(j)) 1≤j≤nmax(f(j)) ,两种情况,第一种是当天卖出,第二种是前j-1天买入,第j天卖出,第二种情况和第j-1天卖出的区别就是第j天的价格减去第j-1天的价格。
f ( j ) = m a x ( 0 , f ( j − 1 ) + p j − p j − 1 ) f(j)=max(0,f(j-1)+p_{j}-p_{j-1}) f(j)=max(0,f(j−1)+pj−pj−1)
一个直观的想法是说维护到目前为止价格的最低点,用上一行减去下一行即可得到最大利润。
5 | 2 | 7 | 13 | 1 | 9 |
---|---|---|---|---|---|
5 | 2 | 2 | 2 | 1 | 1 |
这道题还可以用分治法做,详见上一篇博文。
- 买卖两次
Best Time to Buy and Sell Stock III
设置两个dp数组, f 1 ( i ) f_1(i) f1(i)是第i天第一次卖出最大收益, f 2 ( i ) f_2(i) f2(i)是第i天第二次卖出最大收益。
思路1:
g 1 ( j ) = max 1 ≤ i ≤ j f 1 ( i ) g_1(j)=\max\limits_{1\leq i \leq j}{f_1(i)} g1(j)=1≤i≤jmaxf1(i) 即为前j天的最大收益
f 2 ( j ) = max 1 ≤ i ≤ j { p j − p i + g 1 ( i − 1 ) } f_2(j)=\max\limits_{1\leq i \leq j}\{p_j-p_i+g_1(i-1)\} f2(j)=1≤i≤jmax{ pj−pi+g1(i−1)}
时间