贪心算法(又称贪婪算法)是指,在堆问题求解时,老是作出当前看来是最好的选择。也就是说,不从总体最优上加以考虑,他所作出是在某种意义上的局部最优解。python
贪心算法并不保证会获得最优解,可是在某些问题上贪心算法的解就是最优解。要会判断一个问题可否用贪心算法来计算。算法
假设商店老板须要找零n元钱,钱币的面额有:100元、50元、20元、5元、1元,如何找零使得所需钱币的数量最小?app
t = [100, 50, 20, 5, 1] def change(t, n): # n指的总金额 m = [0 for _ in range(len(t))] # 建立一个和t同样长但全是0的列表 # 这里假设t都是倒序排好的 for i, money in enumerate(t): m[i] = n // money # n整除money:376//100=3 n = n % money # n对money取余:376%100=76 return m, n # 若是到最后找不开,n就是找不开的钱 print(change(t, 376)) # ([3, 1, 1, 1, 1], 0) 即:300+50+20+5+1 # 若是t = [100, 50, 20, 5],则有1块钱找不开, print(change(t, 451)) # ([4, 1, 0, 0], 1)
一个小偷在某个商店发现有n个商品,第i个商品价值vi元,重wi千克。他但愿拿走的价值尽可能高,但他的背包最多只能容纳W千克的东西。他应该拿走哪些商品?函数
对于一个商品,小偷要么把它完整拿走,要么留下。不能只拿走一部分,或把一个商品拿走屡次。(商品为金条)优化
对于一个商品,小偷能够拿走其中任意一部分。(商品为金砂)spa
答:贪心算法对分数背包能够获得最优解,对0-1背包得不到最优解,极可能背包都没法装满。blog
两种背包问题都具备最优子结构性质。对0-1背包问题,考虑重量不超过W而价值最高的装包方案。若是咱们将商品j今后方案中删除,则剩余商品必须是重量不超过W-wj的价值最高的方案(小偷只能从不包括商品j的n-1个商品中选择拿走哪些)。排序
虽然两个问题类似,但咱们用贪心策略能够求解背包问题,而不能求解0-1背包问题,为了求解部分数背包问题,咱们首先计算每一个商品的每磅价值vi/wi。遵循贪心策略,小偷首先尽可能多地拿走每磅价值最高的商品,若是该商品已所有拿走而背包未装满,他继续尽可能多地拿走每磅价值第二高的商品,依次类推,直到达到重量上限W。所以,经过将商品按每磅价值排序,贪心算法的时间运行时间是O(nlgn)。字符串
为了说明贪心这一贪心策略对0-1背包问题无效,考虑下图所示的问题实例。此例包含3个商品和一个能容纳50磅重量的背包。商品1重10磅,价值60美圆。商品2重20磅,价值100美圆。商品3重30磅,价值120美圆。所以,商品1的每磅价值为6美圆,高于商品2的每磅价值5美圆和商品3的每磅价值4美圆。所以,上述贪心策略会首先拿走商品1。可是,最优解应该是商品2和商品3,而留下商品1。拿走商品1的两种方案都是次优的。it
可是,对于分数背包问题,上述贪心策略首先拿走商品1,是能够生成最优解的。拿走商品1的策略对0-1背包问题无效是由于小偷没法装满背包,空闲空间下降了方案的有效每磅价值。在0-1背包问题中,当咱们考虑是否将一个商品装入背包时,必须比较包含此商品的子问题的解与不包含它的子问题的解,而后才能作出选择。这会致使大量的重叠子问题——动态规划的标识。
def fractional_backpack(goods, w): """ 贪心算法——分数背包 :param goods: 商品 :param w: 背包容量 :return: """ m = [0 for _ in range(len(goods))] # [0, 0, 0] total_v = 0 # 总价值 for i, (price, weight) in enumerate(goods): if w >= weight: m[i] = 1 total_v += price w -= weight else: # 不足1 m[i] = w / weight total_v += m[i]*price w = 0 break return m, total_v print(fractional_backpack(goods, 50)) # ([1, 1, 0.6666666666666666], 240.0) 60+100+120*2/3=60+100+80=240
有n个非负整数,将其按照字符串拼接的方式拼接为一个总体。如何拼接可使得获得的整数最大?
例:32,94,128,1286,6,71能够拼接出的最大整数为94716321286128
from functools import cmp_to_key # 传递python2中sort的cmp函数 li = [32, 94, 128, 1286, 6, 71] def xy_cmp(x, y): if x+y < y+x: # 要让大的在前面,这里须要交换 return 1 elif x+y > y+x: return -1 else: return 0 def number_join(li): li = list(map(str, li)) # 转换为字符串:['32', '94', '128', '1286', '6', '71'] # 方法1:cmp函数 li.sort(key=cmp_to_key(xy_cmp)) # 传递进xy_cmp函数 # 方法2:使用快排、冒泡排序等 return "".join(li) print(number_join(li)) # 94716321286128
假设有n个活动,这些互动要占用同一片场地,而场地在某时刻只能供一个活动使用。
每一个活动都有一个开始时间si和结束时间fi(题目中时间以整数表示),表示活动在[si, fi)(左闭右开)区间占用场地。
问:安排哪些活动可以使该场地举办的活动个数最多?
贪心结论:最早结束的活动必定是最优解的一部分。
证实:假设a是全部活动中最早结束的活动,b是最优解中最早结束的活动。
activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)] # 保证活动是按照结束时间排好序的(每次选择最先结束的) activities.sort(key=lambda x: x[1]) # print(activities) def activity_selection(a): """ 活动选择 :param a: 活动 :return: """ res = [a[0]] # 将最先结束的活动放入结果中 for i in range(1, len(a)): # 对列表进行遍历(除去第一个) if a[i][0] >= res[-1][-1]: # 当前活动开始时间 大于等于 res最后一个活动的结束时间 所以不冲突 res.append(a[i]) return res print(activity_selection(activities)) # [(1, 4), (5, 7), (8, 11), (12, 16)]
都是最优化问题。大大提高了解决问题的速度,可是它也有一些优化问题没法解决或获得的不是最优解。
贪心算法存在的问题: