算法思想:分治,动态规划,贪心

       有时候碰到某个编程题,一脸茫然,无从下手,那可能是因为题目问得很大,情况很多,单凭枚举法是解决不了问题的(数据量搞个几千几万啥的)。这时候就需要一个很重要的数学思想,需要牢记于心,必要时想到它,那就是:大问题解决不了,从小问题入手,看一看能不能从小问题慢慢扩展为大问题。数学上就有一个非常鲜明的例子叫数学归纳法。其实算法也是这样,毕竟计算机的世界也是离散的嘛!下面给出我对三种算法设计思想的理解。

 

1.分治 Divide and Conquar如同名字所说,分开逐一击破,最后征服。规模为n的问题,可以转化为两个n/2的问题,或许是转化为n-1与1的问题,亦或许是别的方式。这个思想的要点在于:

  • 对于规模最小的问题是直接能给出答案的。(空集合啦,只有一个元素啦)

  • 规模大的问题与规模小的问题具有类似的结构,因而可以同样处理,不断拆分成最小的可以直接解决的问题。

  • 然后规模小的问题可以想办法合并成规模大的问题,这样不断拆分,拆分至最小并解决,然后不断合并,最后就解决了我们要处理的问题。

 

一个经典的例子是归并排序,也是把一个数组从小到大排序的问题,用上面要点描述就是:

  • 如果数组只有一个元素,那就不用排序了,那就是数组本身。

  • 一个数组可以拆分为两个数组(前一半与后一半),这两个数组分别进行排序处理。

  • 合并,怎么把两个从小到大排好序的数组合并成一个数组呢?或许能够想到,每次比较两个数组开头的数字,把最小的拿出来,就能够完成合并。

这样,拆开,最小问题的解决,合并,最终,我们解决了这个问题!类似的问题有很多,比如快速排序,最大子序列和,选择第k大元素问题等等,大家参考任何一本算法书,多加思考。

 

2. 动态规划dynamic programming。 这类问题通常是询问最优解,比如说值最大啊,方法最省啊等等。大的问题当然不可能直接看出答案,那么我们不妨从小的问题入手?

于是,我们得到了动态规划思想的精髓:先解决小规模问题的最优解,然后逐渐增大问题规模,一般来说,大问题与小问题之间存在千丝万缕的关系(或者说大问题最优解的一部分就是小问题的最优解)然后,我们找到相关的递推公式,这样最终就能够解决大问题了!所以要点在于,保持小问题的解决记录,然后找到那个递推公式,处理好初始的情况,就是这么简单。

 

比如说,背包装物品的问题。我有n件物品,每件物品有重量wi与价值vi,我在背包重量存在上限B的情况下尽可能让背包物品价值最大。那么,我们不妨来分析一下:

  • 首先只允许放第一件物品,简单,如果质量w1不超过B就可以放,获得价值v1,否则价值为0。那么只允许放前二件物品怎么办呢?我其实只有两个选择,不放第二件物品,那和上面讨论一样,放入第二件物品,此时背包重量上限只有B-w2了(如果能放下的话),价值则多了v2,这个问题依旧转化为了只允许放第一件物品的问题。 我们已经发现了递推公式!只要把这两种情况取个好的就行了。

  • 一般的,我们只允许放前N件物品,第N件物品放入或是不放入,都会转化为前N-1件物品的问题(一个质量上限与价值不变,另一个质量上限减少wN,价值增加vN),我们只是挑选价值最大的方案进行记录,只要我们保持记录不同质量上限,只许放前N件物品的最大值,这样N不断增加,最后就解决了我们想要解决的问题!

简单来说,从小问题入手,记录小问题的答案,找到递推公式,这就是动态规划了。类似的还有找最短的路径,最长公共子序列问题等等,大家也不妨多找几个例子,尝试一下,其实不难。

 

当然了背包问题似乎还有一种直观的解决想法:每个物品单位质量的价值vi/wi不同,我不断想办法把性价比最大物品放进去就行了?没错,这也是一种算法设计思想,即贪心法(Greedy),管什么拆分合并,管什么记录解找递推关系,我每一步都最贪婪,不就完了么?只可惜这种思想未必正确,比如装背包这个问题就不适用。然而有时候贪心法确实是一个简单粗暴,却又准确的算法。

比如如果上面背包所有物品价值都一样,那么贪心法就对了:把质量小的不断往背包里塞就行了。贪心法需要验证准确性,然而这就是一个复杂的故事了。

总结一下,其实就是这么一句话:大问题解决不了的时候,从小问题开始入手吧!

猜你喜欢

转载自blog.csdn.net/weixin_42405231/article/details/82383691