vlambda博客
学习文章列表

贪心算法学习,附由贪心算法引发的人生感悟。

贪心算法,又称为贪婪算法,百科中是这样说的:在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择


总结一出一句话,贪心算法的核心是:如何制定贪心策略。

贪心算法解题步骤:

  1. 分析问题,建立贪心策略

  2. 把问题拆分为若干个子问题

  3. 逐一求出各个子问题局部最优解

  4. 根据各个子问题的解,求出原问题的解

实例:

  • 实例1:背包问题

话说在前面,背包问题的终级解决方案是“动态规则”,我们只是用贪心来做讲解

小偷偷东西,但只拿了一个背包,背包的容量有限,怎样在有限的容量内装入价值最大的物品,用贪心算法的思路可以找出三种策略:

1.优先取体积最小的

2.优先取价值最大的

3.优先取单位体积内价值最大的


第一个策略,体积小可能价值也小,大米倒是体积小,小偷装一背包能值几个钱。

第二个策略,优先取价值大的,他家客厅电视价值倒是大,小偷装得下吗?

那就只剩下第三个策略,优先取单位体积内价值最大的,即:价值/容量大的优选选择。


此解法乍一看貌似成立,实则不然,如果出现以下情况,贪心算法就无法求出最优解:

 体积:14,6,6。价值:15,10,10。背包最大容量15

虽然单位价值最大,但是有可能一下把背包装满,而如果先装单位价值小的,反而有可能装得更多。

所以对于背包问题,贪心算法无论选取哪种策略,都无法得到最优解。

注:轮船载货问题、货车装车问题都属于同一类问题,这类问题的最优解,只能用“动态规则”。


  • 实例2:递增数组问题

在牛客网上遇到这样一个简单的题目:

有这样一个Int数组:[1,3,2,4,4,6,5],我们每次可以选择一个连续的区间,让区间的数都加1,问把这个数组变为单调递增,最少需要操作多少次?


题目分析:判断数组中相邻的两个元素大小,后一个元素比前一个大的,则不动。否则后面所有元素都加1,直到整个数组为递增数组。


拆解为子问题后步骤如下:

[1,3,2,4,4,6,5] --> [1,3,4,6,6,8,7] --> [1,3,4,6,7,9,8] -->[1,3,4,6,7,9,10]


1)第三个元素小于第二个元素,那么第三个元素及之后的所有元素需要执行两次+1操作

2)第五个元素和第四个元素都为6,则第五个及以后元素全部执行一次+1操作

3)到此时,只有最后两个元素不是递增,需要执行两次+1操作

以上三个步骤分别执行了2+1+2次操作

由此我们可以得到,每个步骤的局部解:执行次数 = 后面元素的值 - 前面元素的值 + 1。算出每一个局部解,自然可以得出整个问题的解。


代码如下:

private func getCount(array: [Int]) -> Int { var count = 0 for i in 1 ..< array.count { if array[i] <= array[i-1] { count = array[i-1] - array[i] + 1 } } return count}

此代码为Swift语言


感悟:

每个人的一生都有非常多的选择结点和叉路口。或由于人们认知的局限,或由于错误的判断,或由于外界因素的影响,我们往往倾向于选择当前策略下看似最优的路。时间不能倒流,人生不能重来,在这一生中你也许有很多后悔时刻,后悔你当初的选择。但是,你永远无法预知如果当初选择另一条路会是怎么样的一番风景。

人生没有必要得到全局最优解,也不大可能求得全局最优解,只有当你垂垂老矣再回头回顾这一生时,能够从内心说出一句:“此一生无怨无悔”足矣。这也许就是人生的魅力。

我是剑老师,欢迎大家关注【App程序猿】