贪心算法对于一个问题的解决方案是分成多个局部,每次都选出局部最优解,但最终的结果不一定是去全局最优解!
一、贪心算法的应用
解决问题:
我希望用在 最少的广播站 对 最多的州 进行宣传,现在我有“A B C D E”五个广播站,
每个广播站 都覆盖一定数量的州,于是使用贪心算法对 广播站集合 进行遍历处理,
最终选择出 几个广播站 加起来可以覆盖 所有目标州的 来宣传。
# 1. 定义需要覆盖的所有目标州(问题核心目标)
states_needed = {"加州", "内华达州", "犹他州", "亚利桑那州", "新墨西哥州"}
# 2. 定义广播站字典:键=广播站名称,值=该广播站能覆盖的州(集合类型)
stations = {
"广播站A": {"加州", "内华达州"}, # A覆盖2个州
"广播站B": {"内华达州", "犹他州"}, # B覆盖2个州
"广播站C": {"犹他州", "亚利桑那州"}, # C覆盖2个州
"广播站D": {"亚利桑那州", "新墨西哥州"}, # D覆盖2个州
"广播站E": {"加州", "新墨西哥州"} # E覆盖2个州
}
# 3. 用于存储最终选择的广播站(空集合初始化)
final_stations = set()
# 4. 贪心算法核心逻辑:循环直到所有州都被覆盖
while states_needed: # 只要还有未覆盖的州,就继续循环
best_station = None # 每次循环初始化“当前最优广播站”
states_covered = set() # 初始化“当前最优广播站能覆盖的未覆盖州”
# 遍历所有广播站,找出“能覆盖最多未覆盖州”的那个
for station, states in stations.items():
# 计算“当前广播站覆盖的州”与“未覆盖州”的交集(即真正能新增的覆盖范围)
covered = states_needed & states
# 如果当前广播站的新增覆盖范围,比之前记录的最优范围大
if len(covered) > len(states_covered):
best_station = station # 更新“最优广播站”
states_covered = covered # 更新“最优覆盖范围”
# 将本次选出的最优广播站加入最终集合
final_stations.add(best_station)
# 从“未覆盖州”中,减去本次最优广播站覆盖的州(缩小未覆盖范围)
states_needed -= states_covered
# 5. 输出结果:最终选择的广播站
print("最少需要选择的广播站:", final_stations)
# 运行结果通常为:{'广播站A', '广播站C', '广播站D'}(或其他等价组合,如A、D、B)
二、代码分析
前提:变量说明
# 1.全局数据
status_needed #最终要覆盖的所有的州 起初:包含多个州 最终:包含0个州
stations #广播站字典,所有的广播站 全程无变化
final_stations #用于存储最终选择的广播站 起初:包含0个州 最终:包含多个州
# 2.局部数据
best_station #局部最优的广播站 每次for循环起初:none for循环结束:得到本次循环的局部最优解
states_coved #存储“当前广播站覆盖的州”与“未覆盖州”的交集
#每次for循环起初:set() for循环结束:得到本次循环的局部覆盖最多的未覆盖州的覆盖数量
1. 准备数据
# 1. 定义需要覆盖的所有目标州(问题核心目标) 目标数据
states_needed = {"加州", "内华达州", "犹他州", "亚利桑那州", "新墨西哥州"}
# 2. 定义广播站字典:键=广播站名称,值=该广播站能覆盖的州(集合类型) 待处理数据
stations = {
"广播站A": {"加州", "内华达州"}, # A覆盖2个州
"广播站B": {"内华达州", "犹他州"}, # B覆盖2个州
"广播站C": {"犹他州", "亚利桑那州"}, # C覆盖2个州
"广播站D": {"亚利桑那州", "新墨西哥州"}, # D覆盖2个州
"广播站E": {"加州", "新墨西哥州"} # E覆盖2个州
}
2. 初始化存放结果的变量
# 3. 用于存储最终选择的广播站(空集合初始化)
final_stations = set()
[为什么要在一开始就进行声 final_stations 的声明] 如果不在一开始声明final_stations,而是在while循环内部(使用时)声明,会导致每次循环都创建一个新的空集合,最终只能保存最后一轮选中的广播站,丢失之前所有选择
3. 数据处理
# 4. 贪心算法核心逻辑:循环直到所有州都被覆盖
while states_needed: # 只要还有未覆盖的州,就继续循环 只有当states_needed为空时才结束
best_station = None # 每次循环初始化“当前最优广播站”
states_covered = set() # 初始化“当前最优广播站能覆盖的未覆盖州”
# 遍历所有广播站,找出“能覆盖最多未覆盖州”的那个
for station, states in stations.items():
# 计算“当前广播站覆盖的州”与“未覆盖州”的交集(即真正能新增的覆盖范围)
covered = states_needed & states
# 如果当前广播站的新增覆盖范围,比之前记录的最优范围大
if len(covered) > len(states_covered):
best_station = station # 更新“最优广播站”
states_covered = covered # 更新“最优覆盖范围”
# 将本次选出的最优广播站加入最终集合
final_stations.add(best_station)
# 从“未覆盖州”中,减去本次最优广播站覆盖的州(缩小未覆盖范围)
states_needed -= states_covered
[for station, states in stations.items():] 在对字典stations进行遍历的时候会同时解包, stations.items() 会返回类似:[("广播站A", {"加州", "内华达州"}), ("广播站B", { "犹他州"}), ...] 的数据 station 接收当前遍历到的 “键”(即广播站名称,如 “广播站 A”“广播站 B”); states 接收当前遍历到的 “值”(即该广播站覆盖的州集合,如 {"加州", "内华达州"}
4. 输出结果
# 5. 输出结果:最终选择的广播站
print("最少需要选择的广播站:", final_stations)
# 运行结果通常为:{'广播站A', '广播站C', '广播站D'}(或其他等价组合,如A、D、B)