贪心算法的小思考

39 阅读5分钟

贪心算法对于一个问题的解决方案是分成多个局部,每次都选出局部最优解,但最终的结果不一定是去全局最优解!

一、贪心算法的应用

解决问题:

我希望用在 最少的广播站 对 最多的州 进行宣传,现在我有“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)