题目描述
题目分析
在本题中,我们需要优化超市货架上商品的顺序,以便尽可能多地满足顾客的需求。给定的输入包含:
- 货架格子数量 n 和初始商品顺序 s,描述了货架的初始状态。
- 顾客想要购买的商品种类数量 m 和种类列表 c,描述了顾客的需求。
通过重新排列货架上的商品顺序,我们需要使顾客在按顺序查看格子时能够找到尽量多的所需商品,直到顾客遇到空格子或查看完所有格子。
- 货架上的商品和顾客需求的匹配问题:顾客从左到右依次查看商品,遇到匹配的商品则购买并离开;遇到空格子或搜索到最后一个格子则离开。因此,顾客需求的顺序影响不大,我们只需尽量满足每一种需求。
- 重新排序的限制:我们可以在顾客到来前调整商品的顺序,但顾客进入后,商品位置不能再更改。因此,我们的调整方式直接影响了最终出售的商品数目。
- 最大化匹配商品数目:目标是通过调整货架上的商品顺序,使得顾客能找到尽可能多的想要商品。考虑到顾客依次查找的过程,这里可以使用贪心算法来实现。
解题思路
- 计数商品和顾客需求的频率:可以用字典统计货架上每种顾客需求的每种商品的数量。这样我们能够更方便地处理需求和库存的匹配。
- 匹配需求:对于每种商品,能满足的顾客需求数量是该商品在货架上和顾客需求中数量的较小值。依次检查每种商品,计算其满足的顾客需求数量。
- 计算最大可售出商品数量:将所有满足的商品数量累加即为最优调整下最多能卖出的商品数量。
算法选择
我们可以使用贪心算法来解决本题。具体实现步骤为:
- 统计货架上每种商品的数量。
- 统计顾客所需每种商品的数量。
- 对于每种商品,取其在货架上和顾客需求中的数量的最小值,并将其累加到总出售量中。
这样可以在时间复杂度 O(n+m)O(n + m)O(n+m) 内完成计算,其中 n 是货架商品数量,m 是顾客需求的商品种类数。
贪心算法简介
贪心算法(Greedy Algorithm)是一种通过逐步构建局部最优解,期望最终达到全局最优解的算法思想。在贪心算法中,每一步都选择当前情况下能获得的最优解,而不考虑未来可能的后果。这种选择被称为贪心选择。在某些特定问题中,连续的贪心选择可以累积得到问题的全局最优解,但并非所有问题都适合使用贪心算法。
贪心算法的基本特征
- 局部最优:贪心算法在每一步选择上都选择当前的最优解,以期累积达到全局最优解。
- 不可回溯:一旦做出选择就不再回头。这意味着贪心算法没有考虑将来每一步选择的后果,选择一旦做出就不再更改。
- 适用问题:贪心算法适用于一些最优化问题,比如最小生成树问题、背包问题、最短路径问题等。
贪心算法示例
让我们用一个生动的例子来理解贪心算法是如何执行的。假设有一个零钱找零的问题:
例子:零钱找零问题
问题描述:假设你有一些面值为 1 元、5 元和 10 元的硬币,现需要找零给顾客 16 元,如何用最少的硬币数量满足找零需求?
贪心算法的思路:在每一步选择中,优先使用面值最大、能符合当前找零需求的硬币,即每一步都选择可以满足条件的最大面值硬币。
贪心算法步骤:
- 开始时,需求为 16 元。选择最大面值且不超过 16 元的硬币,即 10 元。
- 使用一枚 10 元硬币后,还需找零 6 元。
- 继续选择最大面值的硬币(即 5 元)满足剩余需求 6 元。
- 使用一枚 5 元硬币后,还需找零 1 元。
- 选择 1 元硬币,使用一枚即可凑足剩余的 1 元。
结果:用了 3 枚硬币(10 元、5 元和 1 元)满足了找零 16 元的需求。
贪心算法的执行过程:
| 步骤 | 当前需求 | 选择的硬币 | 余下需求 |
|---|---|---|---|
| 1 | 16 元 | 10 元 | 6 元 |
| 2 | 6 元 | 5 元 | 1 元 |
| 3 | 1 元 | 1 元 | 0 元 |
此时,需求已经完全满足,算法结束。总共用了 3 枚硬币找零 16 元。
贪心算法的优势:
- 在这种情况下,贪心算法的局部最优选择(每次使用最大的硬币)恰好能得到全局最优解(用最少的硬币数)。
贪心算法的局限性
虽然贪心算法在很多问题上很有效,但它并不总是能找到全局最优解。例如,在背包问题中,贪心算法可能无法找到最优解,因为背包问题需要考虑物品的组合,而不仅仅是选择一个最优的局部解。所以,贪心算法仅适用于一部分优化问题。
贪心算法在超市货架调整问题中的应用
在超市货架调整问题中,我们通过贪心算法来选择每种商品能满足顾客需求的最大数量。贪心思路是:
- 计算货架上每种商品的数量和顾客的需求数量;
- 对于每种商品,选择其在货架上和顾客需求中的数量的较小值作为当前最优解;
- 逐一检查所有需求,累加每种商品的最大满足量,即得出最优解。
贪心算法在这里通过局部最优解的选择累积成全局最优解,因此适用于此类货架调整问题。
实现步骤
-
初始化两个字典:
s_count记录货架上各商品的数量,c_count记录顾客需求中各商品的数量。 -
遍历货架字符串 sss 统计每种商品的数量,存入
s_count。 -
遍历顾客需求字符串 ccc 统计每种商品的需求量,存入
c_count。 -
初始化计数器
max_sales,遍历c_count的每种商品:- 如果该商品在货架上存在,则将
s_count[商品]和c_count[商品]的较小值累加到max_sales。
- 如果该商品在货架上存在,则将
-
返回
max_sales作为最大可售出商品数量。
代码实现
def solution(n: int, m: int, s: str, c: str) -> int:
# Step 1: Initialize dictionaries to count occurrences of each item in the shelf and customer needs
s_count = {}
c_count = {}
# Step 2: Count the occurrences of each item on the shelf
for item in s:
s_count[item] = s_count.get(item, 0) + 1
# Step 3: Count the occurrences of each item in customer needs
for item in c:
c_count[item] = c_count.get(item, 0) + 1
# Step 4: Calculate the maximum sales possible
max_sales = 0
for item in c_count:
if item in s_count:
# Step 5: Add the minimum of the shelf count and customer count for each item
max_sales += min(s_count[item], c_count[item])
return max_sales
# Test cases
if __name__ == '__main__':
print(solution(3, 4, "abc", "abcd") == 3) # Expected output: 3
print(solution(4, 2, "abbc", "bb") == 2) # Expected output: 2
print(solution(5, 4, "bcdea", "abcd") == 4) # Expected output: 4