Dynamic Programming 01 - 理解动态规划
我们已经看到了一些优雅的设计原则,例如分而治之,图探索和贪婪的选择,它们为各种重要的计算任务提供了确定的算法。这些工具的缺点是只能用于非常特定类型的问题。现在我们来谈谈算法工艺的两个大锤,即动态编程和线性编程,这两种适用性非常广泛的技术可以在更专门的方法失败时调用。可以预见的是,这种普遍性通常会带来效率上的损失。 很多经典的方法,如 divide-and-conquer, graph exploration, and greedy等, 为各种重要的计算任务提供了确定性的解法。但是这些算法只能用于特定类型的问题。 这里介绍两个算法大杀器: Dynamic programing 和 linear programming. 这两种适用性非常广的算法可以在黔驴技穷时考虑调用(如 the knapsack problem, sequence alignment, and optimal binary search trees)。当然,普遍性往往会带来效率上的损失。 动态规划 动态规划作为一种编程范例,可以从一个例子着手理解:求数列的 maximum-weighted independent sets (MIS, 最大非连续非相邻子集)和, 对于a = [1, 4, 5, 4], 其MIS为{a[1], a[3]} = 8. 如果使用贪心法, 每次都在可选范围内取最大值, 那么就会得到{a[2], a[0]} = 6. 如果使用分而治之法, 把数组分为两半a1 = [1, 4], a2 = [5, 4], 则分别得到MIS{a1[1]}, {a2[0]}, 合并后发现是相邻的, 与要求相悖. 要解决这个问题,关键的步骤是找到基于子问题最优解的最优解:想办法把缩小最优解备选方案的数量,在这个较小的空间中可以直接采取暴力搜索寻找最优解。 对于a = [1, 4, 5, 4], 假设其MIS为S, 假如从最右边的元素开始考虑, a[3] = 4只有属于S和不属于S两种情况 ...