系列笔记链接
《趣学算法(第2版)》读书笔记 Part 1 :如何高效学习算法
《趣学算法(第2版)》读书笔记 Part 3 :贪心算法基础(理论)
《趣学算法(第2版)》读书笔记 Part 4 :贪心算法基础(操作)
贪心算法基础(理论)
对应图书2.1章节内容。
贪心本质
《算法导论》:
贪心算法是“活在当下,看清楚眼前”的方法。贪心算法从问题的初始解开始,一步一步地做出当前最好的选择,逐步逼近问题的目标,尽可能地得到最优解,即使达不到最优解,也可以得到最优解的近似解。
贪心算法在解决问题的策略上“目光短浅”。只根据当前信息就做出选择,且一旦做出选择,不管将来有什么结果,这个选择都不会改变。即,贪心算法并不是从整体最优考虑,它所做出点选择只是在某种意义上的局部最优。在实际应用中,很多问题都可以通过贪心算法得到最优解或最优解的近似解。
在贪心算法中,需要注意以下几个问题:
- 一旦做出选择,不可以回溯。
- 有可能得不到最优解,而是最优解的近似解。
- 选择什么样的贪心策略,直接决定了算法的好坏。
2.1.2 贪亦有道
贪心算法需要遵循的原则。
“君子爱财,取之有道”,在贪心算法中“贪亦有道”。
在遇到具体问题时,人们往往分不清哪些问题能用贪心算法求解,哪些问题不能使用贪心算法。但经过实践发现,利用贪心算法求解答问题往往具有两个重要的性质:贪心选择和最优子结构。只要满足这两个性质就可以使用贪心算法。
-
贪心选择
贪心选择,是指原问题的整体最优解可以通过一系列局部最优的选择得到。
先做出当前最优的选择,将原问题变为一个相似的但规模更小的子问题,而后的每一步都是当前最优的选择。这种选择依赖于已做出的选择,但不依赖于未做出的选择。 -
最优子结构
最优子结构,是指原问题的最优解包含子问题的最优解。
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构是该问题是否可用贪心算法求解的关键。
贪心算法通过一系列的局部最优解(子问题的最优解)得到全局最优解(原问题的最优解),如果原问题的最优解和子问题的最优解没有关系,则求解子问题没有任何意义,就无法采用贪心算法。
例如,对于原问题 S = S= S={ a 1 , a 2 , … , a i , … , a n a_1,a_2,…,a_i,…,a_n a1,a2,…,ai,…,an} ,可通过贪心选择选出一个当前最优解 { a i a_i ai} 之后,转换为求解子问题 S − S- S−{ a i a_i ai} ,继续求解该子问题,最后对所有子问题的最优解进行合并,即可得到原问题的最优解。
2.1.3 贪心算法秘籍
贪心算法秘籍:
-
贪心策略
首先要确定贪心策略,选择当前看上去最好的那个方案。
以挑选苹果为例,如果求解目标是苹果越大越好,那每次就从苹果堆中拿一个最大的苹果,作为局部最优解,贪心策略就是选择当前最大的苹果;如果求解目标是苹果越红越好,那每次就从苹果堆中拿一个最红的苹果,贪心策略就是选择当前最红的苹果。综上所述,根据求解目标不同,贪心策略也会不同。 -
求解过程
根据贪心策略,一步一步地得到局部最优解。
例如,首先选一个最大的苹果记为a1;然后从剩下的苹果堆中选择一个最大的苹果记为 { a 2 a_2 a2} ,以此类推。最后,合并所有的局部最优解,即可得到原问题的最优解 { a 1 , a 2 , … a_1,a_2,… a1,a2,…} 。
补充
换个视角看贪心算法
参考资料:
Leetcode对贪心算法的释义
Leetcode中的贪心算法分栏共有278道题目。
题目难度分三个级别:简单、中等、困难,其中简单
级别共35题。
贪心算法,又称贪婪算法,指在对问题求解时,总是做出在当前看来是最好的选择,就能得到问题的答案。贪心算法需要充分挖掘题目中条件,没有固定的模式,解决有贪心算法需要一定的直觉和经验。
贪心算法不是对所有问题都能得到整体最优解:
- 能使用贪心算法解决的问题具有「贪心选择性质」。
「贪心选择性质」严格意义上需要数学证明。 - 能使用贪心算法解决的问题必须具备「无后效性」。
即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
Leetcode 题目示例(仅题目)
2160. 拆分数位后四位数字的最小和(难度:简单
通过率:85.2%
)
给你一个四位 正 整数
num
。
请你使用num
中的 数位 ,将num
拆成两个新的整数new1
和new2
。
new1
和new2
中可以有 前导 0 ,且 num 中 所有 数位都必须使用。比方说,给你
num = 2932
,你拥有的数位包括:两个2
,一个9
和一个3
。
一些可能的[new1, new2]
数对为[22, 93]
,[23, 92]
,[223, 9]
和[2, 329]
。
请你返回可以得到的new1
和new2
的 最小 和。
示例 1:
输入:num = 2932
输出:52
解释:可行的 [new1, new2] 数对为 [29, 23] ,[223, 9] 等等。
最小和为数对 [29, 23] 的和:29 + 23 = 52 。
示例 2:
输入:num = 4009
输出:13
解释:可行的 [new1, new2] 数对为 [0, 49] ,[490, 0] 等等。
最小和为数对 [4, 9] 的和:4 + 9 = 13 。
提示:
1000 <= num <= 9999
1221. 分割平衡字符串(难度:简单
通过率:84.7%
)
在一个 平衡字符串 中,
'L'
和'R'
字符的数量是相同的。
给你一个平衡字符串s
,请你将它分割成尽可能多的平衡字符串。
注意:分割得到的每个字符串都必须是平衡字符串,且分割得到的平衡字符串是原平衡字符串的连续子串。
返回可以通过分割得到的平衡字符串的 最大数量 。
示例 1:
输入:s = "RLRRLLRLRL"
输出:4
解释:s 可以分割为 "RL"、"RRLL"、"RL"、"RL" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
示例 2:
输入:s = "RLLLLRRRLR"
输出:3
解释:s 可以分割为 "RL"、"LLLRRR"、"LR" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
示例 3:
输入:s = "LLLLRRRR"
输出:1
解释:s 只能保持原样 "LLLLRRRR".
示例 4:
输入:s = "RLRRRLLRLL"
输出:2
解释:s 可以分割为 "RL"、"RRRLLRLL" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
提示:
1 <= s.length <= 1000
s[i] = 'L' 或 'R'
s
是一个 平衡 字符串
大佬营友们的笔记
- 破晓之翼 https://ghdata.blog.csdn.net/article/details/127484441
- 疯狂抓头 http://t.csdn.cn/W9rvY