Skip to content

Instantly share code, notes, and snippets.

@jhuangBU
Created April 11, 2025 18:32
Show Gist options
  • Select an option

  • Save jhuangBU/4ea63c3b03fc00e5f77643188b320119 to your computer and use it in GitHub Desktop.

Select an option

Save jhuangBU/4ea63c3b03fc00e5f77643188b320119 to your computer and use it in GitHub Desktop.
riko-solving-coke-problem

A1

(莉子低下头,仔细看着屏幕上的图片,猫耳轻轻抖动了一下) 主人,这个题目是说,小明在超市买东西,超市有活动:消费每满 M 元(图片例子里是 18 元)就送一瓶可乐。但是呢,超市的机器比较旧,每次结账最多只能送一瓶可乐。所以小明如果想拿到多瓶可乐,就需要把买的东西分成几次来结账。现在我们知道小明买了 N 件商品,价格分别是 A[1] 到 A[N],以及获得可乐的门槛 M 元,目标是计算小明最多能获得多少瓶可乐,对吧喵?

(莉子抬起头,温和的红色眼睛看着你) 嗯... 莉子思考了一下。

首先,要想拿到尽可能多的可乐,我们就要尽可能多地凑出满足“总价 >= M”的结账组合。每一次这样的组合就能换一瓶可乐。

评论区的朋友们提到了动态规划(DP)和背包问题,这确实是一个优化问题。不过,莉子觉得这个问题或许可以用一个更直接的贪心策略来解决喵。

我的想法是这样的:

  1. 处理单件就足够的情况:如果一件商品的价格本身就大于或等于 M,那它自己就能单独结账换一瓶可乐。这种商品肯定要单独结算,因为它无法帮助其他商品达到 M 元的门槛(即使一起结算也只给一瓶),单独结算可以确保这一瓶可乐到手。
  2. 处理需要组合的情况:对于价格小于 M 的商品,我们需要将它们组合起来,使得组合的总价大于或等于 M。为了最大化可乐的数量,我们应该尽可能地利用这些商品。

一个比较好的策略可能是:

  1. 排序:先把所有商品的价格 A 从小到大排序。
  2. 双指针:我们用两个指针,一个 left 指向价格最低的商品,一个 right 指向价格最高的商品。再用一个变量 cokes 记录获得的可乐数量。
  3. 贪心匹配
    • 我们检查当前 right 指向的商品 A[right]。如果 A[right] >= M,那么这件商品单独就能换一瓶可乐。我们就让 cokes 加 1,然后让 right 指针左移一位(right--),表示这件商品已经结算过了。
    • 如果 A[right] < M,说明它需要和其他商品组合。我们尝试将它和当前 left 指向的最便宜的商品 A[left] 组合。
      • 如果 A[left] + A[right] >= M,太好了喵!它们俩可以一起结账换一瓶可乐。我们就让 cokes 加 1,然后 left 指针右移一位(left++),right 指针左移一位(right--),表示这两件商品都用掉了。
      • 如果 A[left] + A[right] < M,这说明最便宜的商品 A[left] 即使和当前最贵的 A[right] 组合,也达不到 M 元。这意味着 A[left]left 右边、right 左边的任何一件商品组合都肯定达不到 M 元。为了最大化可乐数,我们应该优先尝试让 A[right](以及其他较贵的商品)能成功组合。所以,这种情况下,我们只能放弃 A[left] 这件商品(因为它帮不上忙),让 left 指针右移一位(left++),去考虑下一件便宜的商品。A[right] 则保留在原位,等待和后面可能更大的 A[left] 组合。
  4. 循环与结束:我们重复步骤 3,直到 left 指针超过 right 指针(left > right)。
  5. 特殊情况:当 leftright 相遇时(left == right),我们需要检查这最后剩下的唯一一件商品 A[left]。如果 A[left] >= M,那么它自己也能换一瓶可乐,cokes 需要加 1。

(莉子稍微停顿了一下,确认逻辑)

总结一下算法步骤就是:

  1. 读取 N 和 M。
  2. 读取 N 个商品价格 A[i]。
  3. 将数组 A 从小到大排序。
  4. 初始化可乐计数 cokes = 0
  5. 初始化左右指针 left = 0, right = N-1
  6. left <= right 时,执行循环:
    • 如果 left == right:
      • 如果 A[left] >= M,则 cokes++
      • 跳出循环。
    • 如果 A[right] >= M:
      • cokes++
      • right--
    • 否则 (A[right] < M):
      • 如果 A[left] + A[right] >= M:
        • cokes++
        • left++
        • right--
      • 否则 (A[left] + A[right] < M):
        • left++
  7. 输出 cokes

这个贪心策略优先确保单件满足条件的商品能换到可乐,然后尝试用最便宜的和最贵的商品组合,如果组合够 M 就换可乐,如果不够,就放弃最便宜的那个,保留贵的去尝试和次便宜的组合。莉子觉得这样应该能得到最多的可乐数量喵。

主人觉得这个思路怎么样?需要莉子用具体的例子再演示一遍吗?(歪了歪头)

Q2

请写一个简短的python函数演示一下

A2

好的,主人。莉子这就把刚才想的贪心策略用 Python 写成一个简短的函数喵。

(莉子坐在椅子上,小手快速地在虚拟键盘上敲打,猫耳随着思考微微晃动)

def calculate_max_cokes(item_prices, M):
  """
  计算根据贪心策略可以获得的最大可乐数量。

  Args:
    item_prices: 一个包含所有商品价格的列表 (list of int)。
    M: 获得一瓶可乐所需的最低消费金额 (int)。

  Returns:
    可以获得的最大可乐数量 (int)。
  """
  # 1. 先将商品价格从小到大排序
  item_prices.sort()

  # 2. 初始化指针和可乐计数
  left = 0
  right = len(item_prices) - 1
  cokes = 0

  # 3. 双指针贪心匹配
  while left <= right:
    # 特殊情况:只剩最后一件商品
    if left == right:
      if item_prices[left] >= M:
        cokes += 1
      break # 处理完毕,退出循环

    # 情况一:最贵的商品本身就满足条件
    if item_prices[right] >= M:
      cokes += 1
      right -= 1 # 这个商品单独结算
    # 情况二:最贵的商品不满足条件,尝试和最便宜的组合
    elif item_prices[left] + item_prices[right] >= M:
      cokes += 1
      left += 1  # 最便宜的用掉了
      right -= 1 # 最贵的也用掉了
    # 情况三:最便宜的和最贵的加起来也不够
    else:
      # 放弃最便宜的商品,因为它无法帮助当前最贵的商品凑单
      # 保留最贵的商品,尝试和下一个(稍微贵一点的)商品组合
      left += 1

  # 4. 返回结果
  return cokes

# --- 演示一下 ---
# 假设 M = 18
M_threshold = 18
# 假设小明买了这些东西
items = [5, 5, 5, 10, 13, 15, 18, 20]

# 调用函数计算
max_coke_count = calculate_max_cokes(items, M_threshold)

# 输出结果
print(f"商品价格: {items}") # 注意列表内容会在排序后改变
print(f"满 {M_threshold} 元送一瓶可乐")
print(f"最多可以获得 {max_coke_count} 瓶可乐喵。")

# 另一个例子
items2 = [1, 1, 1, 1, 19, 20]
M2 = 18
max_coke_count2 = calculate_max_cokes(items2, M2)
print(f"\n商品价格: {items2}")
print(f"满 {M2} 元送一瓶可乐")
print(f"最多可以获得 {max_coke_count2} 瓶可乐喵。")

# 再一个例子
items3 = [10, 10, 10, 10, 10]
M3 = 18
max_coke_count3 = calculate_max_cokes(items3, M3)
print(f"\n商品价格: {items3}")
print(f"满 {M3} 元送一瓶可乐")
print(f"最多可以获得 {max_coke_count3} 瓶可乐喵。")

(莉子把代码展示给主人看,眼神中带着一丝期待) 主人,这个函数 calculate_max_cokes 接受商品价格列表 item_prices 和金额门槛 M 作为输入,然后返回最多能获得的可乐瓶数。莉子加了几个小例子在后面,方便主人测试和理解喵。

这个实现就是按照刚才讨论的排序加双指针贪心策略来的。希望这个对主人有帮助!(^-^)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment