题目重点:蛋糕依赖糖,糖再依赖蔗糖,共用原料不能分别验证后忘记相加。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 每recipe指定一份产物的正整数ingredients数量,叶子是原料;stock仅存叶原料,单位一致。
- 没有替代食谱、损耗、副产物或可直接消费的成品库存;这些未给的语义需要另定。
解题路线
- expand(item)返回制作一份所需叶原料向量。叶返回{self:1};对每ingredient递归展开、乘需求数量,再按原料相加。
- memo避免共享子recipe反复展开,visiting检测循环。目标quantity再乘最终向量;和stock逐原料比较,缺失库存视0。
- 只比每个子recipe能否制作可能共享消耗同一库存而超用,必须先聚合目标全部需求。函数只判断不扣库存;实际并发制作需原子预留/扣减。
手动走一遍
cake需sugar2/flour1,sugar需cane3,则cake需cane6/flour1;两个cake需cane12/flour2。两分支都需要cane时,各自“库存够”不保证总共够,最终向量相加才能正确。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def requirements(recipes,target,quantity=1):
if type(quantity) is not int or quantity<0:raise ValueError("nonnegative integer quantity required")
visiting=set();memo={}
def expand(item):
if item in memo:return memo[item]
if item in visiting:raise ValueError("recipe cycle")
if item not in recipes:return {item:1}
visiting.add(item);result={}
if not recipes[item]:raise ValueError("nonempty ingredients required")
for ingredient,count in recipes[item].items():
if type(count) is not int or count<=0:raise ValueError("positive integer ingredient amounts required")
for raw,amount in expand(ingredient).items():result[raw]=result.get(raw,0)+count*amount
visiting.remove(item);memo[item]=result;return result
return {raw:amount*quantity for raw,amount in expand(target).items()}
def can_make(recipes,target,stock,quantity=1):
if any(type(v) is not int or v<0 for v in stock.values()):raise ValueError("nonnegative raw stock required")
needed=requirements(recipes,target,quantity)
return all(stock.get(raw,0)>=count for raw,count in needed.items())
复杂度
V recipe/原料节点、E依赖边、B叶原料数,向量memo保守O(EB)运算/O(VB)存储,整数乘数位成本另计;递归深度h需限制。
易错点与追问
容易失分的边界
- 环不是正常recipe,应拒绝,不用无限递归。
- stock有中间成品时需要先消耗成品再展开缺量,本简单合同不处理。
继续深挖
- 已有成品和原料混合库存,依赖拓扑顺序怎样避免过早拆解?
- 多recipe替代方案会怎样变成优化/规划问题?
