Cookie Monster问题:求数组元素全归零的最少移动次数
问题规则
- 初始状态:桌上摆放若干堆饼干,每堆数量为正整数
- 操作规则:每一步选择一个当前存在的堆的大小值
p,对所有饼干数≥p的堆移除p块饼干,小于p的堆保持不变 - 目标:计算吃完所有饼干需要的最少操作次数,同时输出对应的最优操作序列
给定测试用例基准
所有正确实现需要通过以下测试用例,返回匹配的预期步数:
- 用例1:输入
[1,2,3,4,5,6],预期最少步数3,最优序列[4,2,1] - 用例2:输入
[2,3,5,8,13,21,34,55,89],预期最少步数5,最优序列[55,21,8,3,2] - 用例3:输入
[1,10,17,34,43,46],预期最少步数5,最优序列[34,9,8,3,1] - 用例4:输入
[11,26,37,44,49,52,68,75,87,102],预期最少步数6,最优序列[37,31,15,12,7,4] - 用例5:输入
[2**n for n in range(10)](即1、2、4…512的2的幂序列),预期最少步数10,最优序列[512,256,128,64,32,16,8,4,2,1]
原有代码问题
之前写的相邻差贪心逻辑存在本质缺陷:最优操作选择的p值不一定是当前相邻堆的差值,比如用例3最优序列里的9、8,用例4里的31、15等值都不是初始堆的相邻差,贪心局部最优无法得到全局最短序列。
原有错误代码如下:
piles=[11, 26, 37, 44, 49, 52, 68, 75,87, 102] p=piles n=len(piles) moves=0 while sum(piles)>0: for i in range(len(piles)-1,0,-1): if piles[i-1]<piles[i]: piles[i]=piles[i]-piles[i-1] if piles[i] in p: piles.remove(piles[i]) moves+=1
正确求解思路
采用带记忆化的递归搜索+剪枝实现,核心逻辑和优化点如下:
- 状态压缩:每次操作后对堆数组做去重、删除0值、升序排序处理,相同大小的堆操作效果完全一致,0值是已吃完的堆,处理后能大幅降低状态空间
- 记忆化缓存:将处理后的排序元组作为缓存键,避免重复计算相同子问题的结果
- 递归边界:当状态为空(所有堆都吃完),返回0步和空操作序列
- 剪枝优化:
- 合法
p的取值范围只需要覆盖1到当前状态的最大堆值,超过最大值的p没有任何操作效果 - 如果当前搜索到的最优步数已经等于当前状态的不同堆数量,直接终止搜索——理论上不同大小的堆最少需要对应数量的步数才能吃完,不可能得到更优解
- 合法
可运行实现代码
from functools import lru_cache def cookie_monster_solver(piles): # 初始状态预处理:去重、移除0、排序转元组适配缓存要求 init_state = tuple(sorted(set(x for x in piles if x > 0))) @lru_cache(maxsize=None) def dp(state): # 递归边界:无剩余饼干 if not state: return (0, []) max_val = state[-1] min_step = float('inf') best_seq = [] # 遍历所有可能的合法操作p for p in range(1, max_val + 1): # 生成执行p操作后的新状态 new_state = [] for num in state: remain = num - p if num >= p else num if remain > 0: new_state.append(remain) new_state = tuple(sorted(set(new_state))) # 递归求解子问题 sub_step, sub_seq = dp(new_state) total_step = sub_step + 1 # 更新最优解 if total_step < min_step: min_step = total_step best_seq = [p] + sub_seq # 剪枝:达到理论下界直接终止 if min_step == len(state): break return (min_step, best_seq) return dp(init_state) # 全测试用例验证 if __name__ == "__main__": test_cases = [ [1,2,3,4,5,6], [2,3,5,8,13,21,34,55,89], [1,10,17,34,43,46], [11,26,37,44,49,52,68,75,87,102], [2**n for n in range(10)] ] for idx, case in enumerate(test_cases, 1): steps, seq = cookie_monster_solver(case) print(f"测试用例{idx}:最少操作次数{steps},最优序列{seq}")
运行上述代码可以全部匹配给定测试用例的预期结果。
内容的提问来源于stack exchange,提问作者raph c
相关产品推荐
相关产品推荐

