Lights' Out变体游戏回溯算法性能优化技术求助
问题背景与需求
给定最大规格为8×8的m×n棋盘、最多15个棋子(Piece),以及最大值为4的depth变量。棋盘单元格取值范围为0到depth-1,放置棋子时,棋子上标记为"X"的位置会使对应棋盘单元格的值递增1;若单元格当前值为depth-1,递增后变为0。
示例(depth=2):
初始棋盘:
001 011 011
在位置(1,0)放置形状为:
.X XX
的棋子后,棋盘变为:
001 001 101
我们的目标是找出能将棋盘完全清零的所有棋子放置坐标。我实现了带记忆化和剪枝的回溯算法,但处理较大规模输入时速度极慢,核心代码如下:
private boolean isValidPartialSolution(Board board) { // 剪枝逻辑 char[][] currentGrid = board.getGrid(); int depth = board.getDepth(); for (char[] row : currentGrid) { for (char cell : row) { if (cell != '0' && Character.getNumericValue(cell) >= depth) { return false; } } } return true; } private boolean backtrack(Board board, Piece[] pieces, int index) { if (index == pieces.length) { return this.isValid(); } if (!isValidPartialSolution(board)) { System.out.println("Invalid partial solution"); return false; } String key = generateKey(board, pieces, index); if (memoization.containsKey(key)) { System.out.println("Key exists"); return memoization.get(key); // 返回记忆化结果 } for (int row = 0; row < board.getGrid().length; row++) { for (int col = 0; col < board.getGrid()[0].length; col++) { if (board.canPlace(pieces[index], row, col)) { board.placePiece(pieces[index], row, col, depth); if (backtrack(board, pieces, index + 1)) { memoization.put(key, true); return true; } board.removePiece(pieces[index], row, col, depth); } } } memoization.put(key, false); return false; }
恳请提供该算法的优化方案。
优化方案
1. 重构记忆化Key生成逻辑
当前用字符串作为Key,生成与哈希效率极低,建议改为紧凑数值编码:
- 每个单元格值范围是0~3(depth最大为4),用2位二进制即可表示,8×8棋盘仅需128位,可使用两个
long或一个BigInteger存储棋盘状态。 - Key由棋盘状态编码 + 当前处理的棋子索引组成,直接用数值类型作为HashMap的Key,大幅提升哈希与存储效率,避免字符串拼接的开销。
2. 强化剪枝规则
现有剪枝逻辑仅检查单元格值是否越界,补充以下规则提前终止无效分支:
- 剩余棋子覆盖能力不足:统计当前棋盘非零单元格数量,若剩余所有棋子能覆盖的单元格总数小于该值,直接返回false。
- 单元格无法归零预判:对每个非零单元格,计算需要的递增次数(
(depth - val) % depth),若剩余棋子中能覆盖该单元格的数量不足以满足次数,直接返回false。 - 即时无效状态拦截:放置棋子后,若某个单元格的最终值(递增后)无法通过剩余棋子调整至0,直接回溯。
3. 优化棋盘状态修改与回溯
当前用char[][]存储棋盘,类型转换与全量回溯开销大:
- 改用
int[][]存储单元格值,避免Character.getNumericValue的类型转换损耗。 - 放置棋子时记录所有被修改的
(行, 列, 原值),回溯时仅恢复这些位置,无需遍历整个棋盘,大幅减少回溯时间。
4. 启发式调整遍历顺序
放弃固定顺序遍历所有位置,优先选择:
- 能覆盖最多当前非零单元格的位置;
- 能直接将某个非零单元格归零的位置。
通过优先探索更可能接近解的分支,减少无效遍历次数。
5. 相同棋子去重处理
若存在形状完全一致的棋子,避免重复遍历相同放置组合:处理第k个棋子时,若它与前面的某颗棋子相同,跳过与前面棋子放置位置重复的情况,减少重复计算。
6. 预计算有效放置位置
提前为每个棋子计算在当前棋盘上的所有有效放置位置并存储为列表,避免回溯循环中反复调用canPlace检查,减少重复计算量。
内容的提问来源于stack exchange,提问作者TheUnknownChop
相关产品推荐
相关产品推荐

