蓝桥杯软件类考点4:贪心

本文介绍了贪心算法的基本概念,通过实例展示了如何在找零问题、活动选择(如活动选择问题)和数据压缩(如霍夫曼编码)中应用贪心策略。同时,讨论了贪心算法在汽车加油问题中的应用,探讨了局部最优决策如何影响全局解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前提声明

1、什么是贪心算法

        贪心算法是一种在每一步选择中都采取在当前状态下最优的选择,从而希望能够达到全局最优的算法。在每个阶段,贪心算法都会选择局部最优解,而不考虑未来的结果。贪心算法通常不会回溯,即一旦做出了选择,就无法撤销。

特点:

局部最优性: 贪心算法每步选择都是在当前状态下做出的最优选择,即局部最优。
无后效性: 当前的选择不会影响未来的选择,即每个步骤的选择只与当前状态有关。
不回溯: 一旦做出选择,就不再改变。
        贪心算法通常适用于那些具有最优子结构性质的问题,即问题的最优解可以通过一系列局部最优的选择得到。

贪心算法的应用领域很广泛,但并不是所有问题都适合使用贪心算法。在一些问题中,贪心算法可能无法找到全局最优解,因为它没有考虑未来可能的选择带来的影响。

举例:找零问题。假设有一些硬币,面额分别为1元、2元、5元、10元,现在要找零k元。贪心算法可以每次选择面额最大的硬币进行找零,这样通常可以得到较少数量的硬币,尽管并不总是能得到最少数量的硬币

二、习题练习

1、找零问题

假设我们有以下面额的硬币:1元、2元、5元、10元,现在需要找零15元。我们可以使用贪心算法来找到最少数量的硬币。

def greedy_change(coins,target):
    coins.sort(reverse=True) # 将硬币额度降序排序
    change = [] #用于存储找零的硬币列表

    for coin in coins:
        while target >= coin:
            target -= coin
            change.append(coin)

    if target == 0:
        return change
    else:
        return None

if __name__ == '__main__':
    coins = [1,2,5,10]
    target = 15

    result = greedy_change(coins,target)

    if result is not None:
        print(f'找零{target}元的最少硬币数为:{len(result)},硬币列表为{result}')
    else:
        print(f'找零失败,无法凑成目标金额')



这个程序使用贪心算法,每次选择面额最大的硬币,直到凑够目标金额。在这个例子中,程序会输出找零15元的最少硬币数量及硬币列表。贪心算法在这种情境下通常能够得到较好的结果,但并不总是能找到全局最优解。

2、活动选择

一个典型的实际问题是活动选择问题(Activity Selection Problem)。在这个问题中,有一组活动,每个活动都有一个开始时间和结束时间。活动之间不能重叠,即同一时刻只能进行一个活动。目标是找到一个最大的互不重叠的活动集合。

活动一:开始时间13:30,结束时间16:30

活动二:开始时间15:30,结束时间17:30

活动三:开始时间12:00,结束时间18:00

活动四:开始时间17:30,结束时间19:30

活动五:开始时间20:30,结束时间21:30

活动六:开始时间17:30,结束时间21:30

求解在12:00到21:30这个时间段最大的互不重叠的活动集合。

'''
activities.sort(key=lambda x: x[2])这行代码使用了Python的sort函数进行排序,其中的key参数指定了排序的依据。
activities 是一个包含活动信息的列表,每个元素是一个元组,包含活动的名称、开始时间和结束时间。
lambda x: x[2] 是一个匿名函数,用于定义排序的关键字。在这里,x 代表列表中的一个元素,x[2] 表示取该元素的第三个元素,即活动的结束时间。
因此,key=lambda x: x[2] 的作用是告诉 sort 函数按照每个元素的结束时间来排序。
'''

def activity_selection(activities):
    # 按照活动的结束时间升序排序
    activities.sort(key=lambda x: x[2]) # 取该元素的第三个元素
    # 初始化已选活动列表,将第一个活动添加到列表中
    selected_activities = [activities[0]]

    # 记录上一个已选活动的结束时间
    last_end_time = activities[0][2]

    # 从第二个活动开始遍历
    for activity in activities[1:]:
        start_time,end_time = activity[1],activity[2] # 获取开始时间和结束时间

        # 判断当前活动的开始时间是否在上一个活动的结束时间之后
        if start_time >= last_end_time:
            # 如果是,则将当前活动添加到已选活动列表中,并更新上一个活动的结束时间
            selected_activities.append(activity)
            last_end_time = end_time

    return selected_activities

if __name__ == '__main__':
    activities = [
        ("活动一", "13:30", "16:30"),
        ("活动二", "15:30", "17:30"),
        ("活动三", "12:00", "18:00"),
        ("活动四", "17:30", "19:30"),
        ("活动五", "20:30", "21:30"),
        ("活动六", "17:30", "21:30"),
    ]
    # 将时间字符串转换为元组,便于排序
    act = [(name,start,end) for name,start,end in activities]

    result = activity_selection(act)
    print(f'最大互不重叠的集合为:{result}')

3、霍夫曼码


一个实际问题是霍夫曼编码(Huffman Coding),它是一种用于数据压缩的贪心算法。霍夫曼编码主要用于将字符转换为变长的二进制编码,以便在传输和存储数据时减小数据的大小。

将字符串abcdefgh转换为对应的霍夫曼码。

'''
一个实际问题是霍夫曼编码(Huffman Coding),它是一种用于数据压缩的贪心算法。霍夫曼编码主要用于将字符转换为变长的二进制编码,
以便在传输和存储数据时减小数据的大小。
将字符串abcdefgh转换为对应的霍夫曼码。
'''
import heapq
from collections import defaultdict

def huffman_coding(data):
    # 统计字符频率
    frequency = defaultdict(int)
    for char in data:
        frequency[char] += 1

    # 构建优先队列(最小堆)用于构建霍夫曼树
    heap = [[weight, [char, ""]] for char, weight in frequency.items()]
    heapq.heapify(heap)

    # 构建霍夫曼树
    while len(heap) > 1:
        lo = heapq.heappop(heap)
        hi = heapq.heappop(heap)
        for pair in lo[1:]:
            pair[1] = '0' + pair[1]
        for pair in hi[1:]:
            pair[1] = '1' + pair[1]
        heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])

    # 返回霍夫曼编码
    return heap[0][1:]

if __name__ == '__main__':
    # 示例
    data = "abcdefgh"
    huffman_code = huffman_coding(data)

    print("字符对应的霍夫曼编码:")
    for char, code in huffman_code:
        print(f"{char}: {code}")

4、汽车加油

汽车加油问题:
假设有一辆汽车要从起点出发前往终点,途中有若干个加油站。汽车的油箱有限,每次加满油能够行驶一定的距离。我们知道每个加油站的位置和它能提供的汽油数量。
目标是选择合适的加油站,以确保汽车能够顺利到达终点。

示例:汽车从起点0出发,初始油量10,加油站信息为(3, 4)和(7, 3),终点为目标位置15,求最少加油次数?

import heapq

def min_refuel_stops(target, start_fuel, stations):
    # 使用最大堆存储每个加油站的汽油数量
    max_heap = []

    # 记录已加油次数
    refuel_count = 0

    # 当前汽车的位置
    current_position = 0

    # 当前汽车的剩余油量
    current_fuel = start_fuel

    # 遍历每个加油站
    for station in stations:
        station_position, station_fuel = station

        # 判断是否需要加油
        while current_position + current_fuel < station_position:
            # 如果无法到达当前加油站,选择之前加油站中提供最大汽油的加油站进行加油
            if not max_heap:
                return -1  # 无法到达终点

            current_fuel += -heapq.heappop(max_heap)
            refuel_count += 1

        # 将当前加油站的汽油数量存入最大堆
        heapq.heappush(max_heap, -station_fuel)

    # 判断是否需要加油到达终点
    while current_position + current_fuel < target:
        # 如果无法到达终点,选择之前加油站中提供最大汽油的加油站进行加油
        if not max_heap:
            return -1  # 无法到达终点

        current_fuel += -heapq.heappop(max_heap)
        refuel_count += 1

    return refuel_count

if __name__ == '__main__':
    # 示例:汽车从起点0出发,初始油量10,加油站信息为(3, 4)和(7, 3),终点为目标位置15
    target_distance = 15
    initial_fuel = 10
    fuel_stations = [(3, 4), (7, 3)]

    result = min_refuel_stops(target_distance, initial_fuel, fuel_stations)
    print("最少加油次数为:", result)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值