给定食谱集合与已拥有食材集合,求解“添加哪种食材可解锁最多食谱”的高效算法选型
解决方案:动态计数+反向映射的高效查询架构
针对你提出的问题——在大规模食谱和食材集合中,快速找到添加后能解锁最多食谱的食材,同时支持动态添加/移除食材的需求,我设计了一套基于反向映射+状态维护的解决方案,完全满足你的性能要求。
核心思路拆解
首先明确问题本质:添加食材X后能完成的食谱,是那些当前仅缺失X的食谱(如果一个食谱缺失多种食材,添加X只是减少缺口数,但无法直接完成它)。我们的目标就是实时统计每个食材X对应的“仅缺X的食谱数量”,并能快速找到最大值对应的X。
关键数据结构设计
我们需要维护以下几个核心结构:
ingredient_to_recipes:反向映射字典,键是食材名称,值是包含该食材的所有食谱列表。用于快速找到某食材关联的所有食谱,是动态更新的基础。missing_count:字典,键是食谱名称,值是该食谱当前缺失的食材数量。用于跟踪每个食谱的完成进度。candidate_scores:字典,键是食材名称,值是添加该食材后能完成的食谱数量(即当前仅缺该食材的食谱数)。这是我们查询的核心数据。score_to_ingredients(可选优化):字典,键是分数值,值是对应分数的食材列表。用于O(1)时间找到当前分数最高的食材,避免遍历所有食材。
预处理步骤
预处理可以一次性完成,复杂度可忽略:
- 构建
ingredient_to_recipes:遍历所有食谱,将每个食材关联到对应的食谱列表,总时间复杂度O(M)(M是所有食谱的食材总数,因单食谱最多10种食材,M=10*N,N为食谱数)。 - 初始化
missing_count:对每个食谱,统计其中未拥有的食材数量,时间复杂度O(M)。 - 初始化
candidate_scores和score_to_ingredients:遍历每个食谱,如果missing_count[R] == 1,找到它唯一缺失的食材X,将candidate_scores[X] +=1,同时把X加入score_to_ingredients[1](如果该键不存在则创建)。时间复杂度O(M)。
动态更新流程
当添加食材X(将ingredients[X]从false改为true)
- 从
ingredient_to_recipes中取出所有包含X的食谱列表。 - 遍历每个关联食谱R:
- 如果X原本是R的缺失食材(即之前
ingredients[X]为false):- 计算新的缺失数:
new_missing = missing_count[R] - 1 - 若原缺失数
missing_count[R] == 1:- 这个食谱原本仅缺X,现在完成了,所以
candidate_scores[X] -=1,同时更新score_to_ingredients(把X从原分数列表移除,若列表为空则删除该分数键)。
- 这个食谱原本仅缺X,现在完成了,所以
- 若原缺失数
missing_count[R] == 2:- 现在食谱仅缺另一种食材Y(遍历R的10种食材即可快速找到Y),所以
candidate_scores[Y] +=1,更新score_to_ingredients(把Y加入新分数的列表)。
- 现在食谱仅缺另一种食材Y(遍历R的10种食材即可快速找到Y),所以
- 更新
missing_count[R]为new_missing。
- 计算新的缺失数:
- 如果X原本是R的缺失食材(即之前
当移除食材X(将ingredients[X]从true改为false)
- 从
ingredient_to_recipes中取出所有包含X的食谱列表。 - 遍历每个关联食谱R:
- 如果X原本是R的已拥有食材(即之前
ingredients[X]为true):- 计算新的缺失数:
new_missing = missing_count[R] + 1 - 若原缺失数
missing_count[R] == 0:- 这个食谱现在仅缺X,所以
candidate_scores[X] +=1,更新score_to_ingredients(把X加入对应分数的列表)。
- 这个食谱现在仅缺X,所以
- 若原缺失数
missing_count[R] == 1:- 这个食谱原本仅缺Y,现在缺Y和X,所以
candidate_scores[Y] -=1,更新score_to_ingredients(把Y从原分数列表移除)。
- 这个食谱原本仅缺Y,现在缺Y和X,所以
- 更新
missing_count[R]为new_missing。
- 计算新的缺失数:
- 如果X原本是R的已拥有食材(即之前
查询操作
- 若使用
score_to_ingredients:直接取最大的分数键(可维护一个变量跟踪当前最大分数,避免每次取max()),对应的食材列表中的任意一个就是答案,时间复杂度O(1)。 - 若未使用优化:遍历
candidate_scores找到值最大的食材,时间复杂度O(K)(K为食材总数),适合食材数量不大的场景。
复杂度分析
- 预处理:O(M),线性时间,完全可忽略。
- 动态更新:每次操作处理的食谱数等于该食材关联的食谱数,每个食谱的处理是常数时间(因单食谱最多10种食材),所以每次更新的时间复杂度O(D)(D为关联食谱数)。
- 查询:O(1)(优化后)或O(K)(未优化)。
问题归类说明
这个问题不属于NP问题,它是一个典型的动态计数问题,通过维护中间状态可以用多项式时间高效解决,不存在指数级复杂度的瓶颈。
内容的提问来源于stack exchange,提问作者Cody E
相关产品推荐
相关产品推荐

