贪心算法
本文最后更新于:2020年4月25日 下午
📰 基本概念
对于一组数据,定义了限定值(比如重量不大于100KG)和期望值(物品总价值最高),希望从中选取几个数据,在满足限定值的情况下,期望值最大。
贪心算法每一步都会采取当前状态下最有利的选择,从而希望得到最优解。但由于前面的选择会影响后面的选择,有时候期望终究是期望,无缘全局最优解。
🔎 实例分析
(1)分糖果
- 向n个孩子分m个糖果,每个孩子对糖果的需求不一样,每个糖果的大小不一样,n>m。只有当分到的糖果大于其需求时,该孩子才得到满足。如何分配糖果,尽可能满足最多数量的孩子。
- 限制值:糖果个数m,期望值:得到满足的孩子数量
- 按照糖果需求高低和糖果大小分别对孩子和糖果进行排序,从
糖果需求最小的孩子
开始分配糖果,然后每次从剩下的孩子中选择对糖果需求最小的孩子,从剩下的糖果中拿给他满足其需求的最小糖果。
(2)钱币找零
- 有 1 元、2 元、5 元、10 元、20 元、50 元、100 元这些面额的纸币,它们的张数分别是 c1、c2、c5、c10、c20、c50、c100。现在要用这些钱来支付 K 元,最少要用多少张纸币呢?
限制值:支付的金额K,期望值:所用纸币的数量
从
面额最大的纸币
开始,不够的话,面额次大的补齐
(3)区间覆盖
- 有n个期间,区间的起始端点和结束端点分别是[l1, r1],[l2, r2],[l3, r3],……,[ln, rn]。从这 n 个区间中选出一部分区间,这部分区间满足两两不相交(端点相交的情况不算相交),最多能选出多少个区间呢?
- 限制值:选中的区间两两不相交,期望值:选中的区间数量
- 根据起始端点从小到大对区间进行排序,每次选择
左端点跟之前已经覆盖的区间不重合的、且右端点又尽量小
的区间,这样可以让剩下的未覆盖区间尽可能的大,就可以放置更多的区间。 - 场景:任务调度,教师排课
(4)霍夫曼编码
- 不等长编码,出现频率低的字符编码长度大,出现频率高的字符编码长度小
- 将每个字符看作一个节点,并且辅带着把频率放到优先级队列中。我们从队列中取出
频率最小的两个节点
A、B,然后新建一个节点 C,把频率设置为两个节点的频率之和
,并把这个新节点 C 作为节点 A、B 的父节点。最后再把 C 节点放入到优先级队列中。重复这个过程,直到队列中没有数据。 - 左子节点的边标记为 0,右子节点的边标记为 1,从根节点到叶节点的路径就是叶节点对应字符的霍夫曼编码。
(5)思考题
在一个非负整数 a 中,我们希望从中移除 k 个数字,让剩下的数字值最小,如何选择移除哪 k 个数字呢?
限制值:删除数字的位数,或者说删除后还剩几位,期望值:删除数字后的值
正确解法:从最高位开始,比较低一位数字,高位数字大,则舍弃;低位数字大,则向右移一位继续比较两个数字,直到高位大于低位达到移除条件,循环k次
class Solution: def deleteNumber(self, a: int, k: int) -> int: temp=list(str(a)) # 将字符串转换为列表 for i in range(k): for j in range(len(temp)-1): if temp[j] > temp[j+1]: del temp[j] break # 跳出第二个for循环 return(''.join(temp)) a=Solution() print(a.deleteNumber(76394288,3))
!!!错误解法:对a的数字进行排序,删除最大的K个数字。
class Solution: def deleteNumber(self, a: int, k: int) -> int: temp=[] temp2=[] length=len(str(a)) for i in str(a): temp.append(i) temp.sort() for i in str(a): if i in temp[:length-k]: temp2.append(i) return(int(''.join(temp2))) # 列表转字符串,再转整型 a=Solution() print(a.deleteNumber(7639428,3))
假设有 n 个人等待被服务,但是服务窗口只有一个,每个人需要被服务的时间长度是不同的,如何安排被服务的先后顺序,才能让这 n 个人总的等待时间最短?
- 限制值:等待服务的人数,服务窗口数量,期望值:总的等待时长
- 按需求时间排序,从短到长开始。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!