贪心算法学习,附由贪心算法引发的人生感悟。
贪心算法,又称为贪婪算法,百科中是这样说的:在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择
总结一出一句话,贪心算法的核心是:如何制定贪心策略。
贪心算法解题步骤:
分析问题,建立贪心策略
把问题拆分为若干个子问题
逐一求出各个子问题局部最优解
根据各个子问题的解,求出原问题的解
实例:
实例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程序猿】