一、贪心算法
- 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的是在某种意义上的局部最优解。
- 贪心算法并不会保证会得到最优解,但是在某些问题上贪心算法的解就是最优解。要学会判断一个问题能否用贪心算法来计算。
1. 找零问题
假设商店老板需要找零n元钱,钱币的面额有:100元,50元、20元、5元和1元,(无限多张)如何找零使得所需要的钱币的数量最少?
t = [100,50,20,5,1]
def change(t, n): # n表示需要找零多少
m = [0 for _ in range(len(t))] # m = [0 0 0 0 0] # 表示数量
for i, money in enumerate(t):
m[i] = n // money
n = n % money
return m, n
print(change(t, 376))
输出结果如下:
2. 背包问题
一个小偷在某个商店发现有n个商品,第i个商品价值vi元,重wi千克。他希望拿走的价值尽量高,但他的背包最多只能容纳W千克的东西。他应该拿走哪些商品?
背包问题可以分为两种类型的问题:0-1背包 + 分数背包
- 0-1背包:对于一个商品,小偷要么把它完整拿走,要么留下,不能之拿走一部分,或把一个商品拿走多次。
- 分数背包:对于一个商品,小偷可以拿走其中任意一部分。(商品为金砂)
举例:
- 商品1:v1 = 60 ,w1 = 10
- 商品2:v2 = 100,w1 = 20
- 商品3:v3 = 120, w3 = 30
- 背包容量: W = 50
对于0-1背包,贪心算法不能得到最优解。(选择0-1背包最后可能出现包装不满的情况,可能剩下好多容量)
对于分数背包,贪心算法可以得到最优解。(选择最贵的直至包里不剩位置)
分数背包的代码实现:
def fractional_backpack(goods, W): # W表示最大负重
# 贪心是指拿走单位重量更值钱的商品
goods.sort(key=lambda x: x[0] / x[1], reverse=True)
m = [0 for _ in range(len(goods))] # m = [(), (), ()] # m返回的指是对应的排好序之后的
total_v = 0 # 总价值
for i, (prize, weight) in enumerate(goods):
if W >= weight:
m[i] = 1
W -= weight
total_v += prize
else:
m[i] = W / weight
total_v += m[i] * prize
W = 0
break
return m, total_v
print(fractional_backpack(goods, 50))
3. 数字拼接问题
- 有n个非负整数,将其按照字符串拼接的方式拼接为一个整数。如何拼接可以使得得到的整数最大?
- 例如:32,94,128,1286,6,71可以拼接城的最大整数为94716321286128
方法一:可以通过Python中内置的 functools import cmp_to_key进行书写
from functools import cmp_to_key
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_1(li):
li = list(map(str, li))
li.sort(key=cmp_to_key(xy_cmp))
return "".join(li)
方法二:使用冒泡排序进行书写:
def number_join_2(li):
li = list(map(str, li))
for i in range(len(li)):
for j in range(i, len(li)):
if li[i] + li[j] <= li[j] + li[i]:
li[i], li[j] = li[j], li[i]
return "".join(li)
4. 活动选择问题
- 假设有n个活动,这些活动要占用同一片场地,而场地在某时刻只能供一个活动使用。
- 每个活动都有一个开始时间si和结束时间fi(题目中时间以整数表示),表示活动在[si, fi)遇见占用场地。
- 问:安排哪些活动能够使该场地举办的活动的个数最多?
贪心结论:最先结束的活动一定是最优解的一部分。
证明:假设a是所有活动中最先结束的活动,b是最优解中最先结束的活动。
- 如果a = b,结论成立
- 如果a ≠ b,则b的结束时间一定晚于a的结束时间,则此时用a替换最优解中的b,a一定不与最优解中的其他活动时间重叠,因此替换后的解也是最优解。
示例代码如下所示:
def activity_selection(a):
res = [a[0]] #res指最优解,首先只有最先开始的活动
for i in range(1, len(a)):
if a[i][0] >= res[-1][1]: # a[i][0] 表示当前活动的开始时间 res[-1][1]表示最后一个活动的结束时间
# 满足上述条件则不冲突
res.append(a[i])
return res
print(activity_selection(activities))