贪心算法入门

60 阅读6分钟

认识贪心算法

在求最优解的问题中,以某种贪心标准,从状态的最初始找到每一步最优解,通过多次的贪心求解,最终得到整个问题的最优解,此种解题的方法为贪心算法。可见贪心算法并不是一种固定的算法,而是根据问题的条件而产生的一种解决问题的思维模式。

最优子结构性质。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质,也称此问题满足最优性原理。也就是说,从局部最优能扩展到全局最优。

由定义可知,贪心算法是由局部的最优解,得到总体的最优解,因此在使用贪心算法之前,要先判断问题是否适合使用贪心算法。

解题

有了上面最基础的解决问题的思想之后我们再来看MarsCode的这一题:

9. 超市里货物架的调整 问题描述 在一个超市里,有一个包含 nn 个格子的货物架,每个格子中放有一种商品,商品用小写字母 a 到 z 表示。当顾客进入超市时,他们会依次从第一个格子查找到第 nn 个格子,寻找自己想要购买的商品。如果在某个格子中找到该商品,顾客就会购买它并离开;如果中途遇到一个空格子,或查找完所有格子还没有找到想要的商品,顾客也会离开。

作为超市管理员,你可以在顾客到来之前重新调整商品的顺序,以便尽可能多地出售商品。当第一个顾客进入后,商品位置不能再调整。你需要计算在最优调整下,最多可以卖出多少件商品。输入变量说明:

  • n:货物架的格子数
  • m:顾客想要购买的商品种类数
  • s:货物架上商品的初始顺序
  • c:顾客想要购买的商品种类

问题分析

题目描述*

场景:超市的货架上有  n  个格子,每个格子中放有一种商品(用小写字母 a-z 表示),顾客会按顺序从第一个格子查找到第  n  个格子,寻找自己想要的商品。

顾客行为

  1. 如果顾客找到想要的商品,立即购买并离开。

  2. 如果途中遇到一个空格子或遍历完所有格子仍未找到商品,也会直接离开。

超市管理员的任务:可以在顾客到来之前重新调整货架上商品的顺序,以便尽可能多地满足顾客的需求。

限制:当第一个顾客进入后,商品的位置不能再调整。

目标:在最优调整下,计算超市最多能卖出多少件商品。

最优子结构

刚刚我们说了判断一个题目是否使用贪心算法的思路那就是分析问题是否具有最优子结构性质。我们就分析问题是否具有最优子结构性质

问题描述简化:

  1. 货架上的商品可以重新调整顺序。

  2. 顾客依次查找商品,购买到商品后离开。

  3. 我们需要调整顺序,使得满足的顾客需求最大化。

最优子结构性质分析

• 分解问题:

• 假设我们已经解决了前  k  个格子的商品安排问题,使得这部分商品可以最大化满足顾客的需求。

• 对于剩下的  n-k  个格子,我们只需要继续安排这些商品以最大化满足剩余的顾客需求。

• 这表明:前  k  格子问题的最优解不会影响后续问题的最优解。

• 子问题独立性:

• 如果我们将顾客需求的每种商品分开处理,比如顾客需要 “a” 的数量为  c_a ,我们只需要考虑货架上有多少个 “a”,其他商品的安排不会影响 “a” 的计算。

• 这表明:每种商品的需求计算是独立的,可以单独处理。

• 子问题最优性:

• 如果我们调整货架的前部分商品,使其能够尽可能多地满足顾客的一部分需求,这部分最优解不会因为后续货架商品的调整而被改变。

这表明:最优子结构性质成立

代码实现

遍历货架上的商品s。用一个字典 shelf_count 来记录货架上每种商品的数量:如果商品  item已存在于字典中,数量加 1;如果商品item不在字典中,初始化为 1。

shelf_count = {}
for item in s:
    shelf_count[item] = shelf_count.get(item, 0) + 1

统计顾客需求:遍历顾客需求商品  c 。用一个字典 customer_demand 来记录顾客对每种商品的需求量:如果商品  item  已存在于字典中,需求量加 1。然后再计算最多能售出的商品数量如果商品  item  不在字典中,初始化为 1。遍历顾客需求字典 customer_demand,对每种商品item:如果该商品存在于货架库存 shelf_count 中:售出的数量为顾客需求量  demand  和货架库存量  shelf_count[item]  的最小值。如果该商品不在货架上,则无法满足需求。再累计所有售出的商品数量

customer_demand = {}
for item in c:
    customer_demand[item] = customer_demand.get(item, 0) + 1
sold_count = 0
for item, demand in customer_demand.items():
    if item in shelf_count:
        sold_count += min(demand, shelf_count[item])

代码

def solution(n: int, m: int, s: str, c: str) -> int:
    # 步骤 1: 统计货架上的商品库存
    # 使用一个字典来记录每种商品在货架上的数量
    shelf_count = {}
    for item in s:
        shelf_count[item] = shelf_count.get(item, 0) + 1  # 如果字典中不存在该商品,则初始化为 0,再加 1

    # 步骤 2: 统计顾客的需求
    # 使用一个字典来记录每种商品的需求数量
    customer_demand = {}
    for item in c:
        customer_demand[item] = customer_demand.get(item, 0) + 1  # 同样,如果字典中不存在该商品,则初始化为 0,再加 1

    # 步骤 3: 计算可以售出的最大商品数量
    sold_count = 0  # 初始化售出商品数量为 0
    for item, demand in customer_demand.items():  # 遍历顾客的每种需求
        if item in shelf_count:  # 如果货架上有该商品
            # 售出的数量是该商品的需求量和库存量的最小值
            sold_count += min(demand, shelf_count[item])
    
    return sold_count  # 返回售出商品的总数量

# 测试用例
if __name__ == "__main__":
    # 测试用例 1: 顾客需求中有一个商品不在货架上
    print(solution(3, 4, "abc", "abcd") == 3)  # 货架为 "abc",顾客需求为 "abcd",最多卖出 3 件

    # 测试用例 2: 顾客需求完全匹配货架上的某些商品
    print(solution(4, 2, "abbc", "bb") == 2)   # 货架为 "abbc",顾客需求为 "bb",最多卖出 2 件

    # 测试用例 3: 顾客需求与货架上的所有商品完全匹配
    print(solution(5, 4, "bcdea", "abcd") == 4)  # 货架为 "bcdea",顾客需求为 "abcd",最多卖出 4 件