求通过置0匹配行列目标和的网格问题递归求解算法
矩阵置0满足行列目标和的回溯算法完善方案
问题描述
给定n阶二维矩阵board,以及对应行目标和数组、列目标和数组,仅通过将矩阵中元素置0的操作,使得操作后的矩阵每行元素和等于对应行目标值,每列元素和等于对应列目标值。
给出的测试用例如下:
- 初始board:
board = [ [5,9,4,6,1], [6,2,3,6,7], [3,4,8,4,4], [2,2,6,4,6], [7,3,1,6,5] ]
- 行目标值:
row_goal = [24,13,15,8,12] - 列目标值:
col_goal = [8,11,13,18,22] - 预期输出:
board = [ [5,9,4,6,0], [0,0,0,6,7], [3,0,8,0,4], [0,2,0,0,6], [0,0,1,6,5] ]
原代码存在的问题
你的回溯思路方向正确,但存在几个关键逻辑漏洞:
- 缺少
row_sum()、col_sum()的实现逻辑,且每次全量计算矩阵和效率极低,也没有提前剪枝的判断逻辑 - 递归调用逻辑错误:对同一个单元格重复调用了两次
solve_board,没有明确区分「置0」和「保留原值」两种选择的处理逻辑 - 没有对
visited数组做回溯处理,会导致后续回溯路径无法访问已经遍历过的单元格
修正后完整实现代码
# 初始化全局变量(也可封装为类避免全局变量,此处和原有写法逻辑保持兼容) board = [ [5,9,4,6,1], [6,2,3,6,7], [3,4,8,4,4], [2,2,6,4,6], [7,3,1,6,5] ] row_goal = [24,13,15,8,12] col_goal = [8,11,13,18,22] board_len = len(board) # 预计算初始行列和,维护当前实时行列和,避免重复计算 init_row_sum = [sum(row) for row in board] init_col_sum = [sum(col) for col in zip(*board)] curr_row_sum = init_row_sum.copy() curr_col_sum = init_col_sum.copy() def is_cell_valid(x_coord, y_coord): return 0 <= x_coord < board_len and 0 <= y_coord < board_len def choose_cell(x,y,visited): # 优先从当前位置往后找,减少重复遍历 for i in range(x,board_len): start_j = y if i == x else 0 for j in range(start_j, board_len): if visited[i][j] == 0 and is_cell_valid(i,j): return i,j # 没找到再从头遍历 for i in range(0,board_len): for j in range(0, board_len): if visited[i][j] == 0 and is_cell_valid(i,j): return i,j return -1,-1 def solve_board(x, y, visited): # 终止条件1:所有行列和都符合目标 if curr_row_sum == row_goal and curr_col_sum == col_goal: return True # 终止条件2:没有未访问的单元格,仍未满足要求 x,y = choose_cell(x,y, visited) if x == -1: return False # 标记当前单元格为已访问 visited[x][y] = 1 temp = board[x][y] # 选择1:将当前单元格置0 curr_row_sum[x] -= temp curr_col_sum[y] -= temp board[x][y] = 0 # 提前剪枝:置0后行列和不能小于目标值,否则直接放弃该路径 if curr_row_sum[x] >= row_goal[x] and curr_col_sum[y] >= col_goal[y]: if solve_board(x, y, visited): return True # 回溯置0的选择 board[x][y] = temp curr_row_sum[x] += temp curr_col_sum[y] += temp # 选择2:保留当前单元格原值,直接递归 if solve_board(x, y, visited): return True # 两种选择都失败,回溯visited标记,返回失败 visited[x][y] = 0 return False # 调用执行 visited = [[0]*board_len for _ in range(board_len)] if solve_board(0,0, visited): print("求解成功,结果为:") for row in board: print(row) else: print("无符合要求的解")
逻辑说明
- 新增实时行列和维护,避免每次递归全量计算矩阵和,大幅提升运行效率
- 对每个单元格明确处理两种选择:置0/保留原值,每种选择执行后递归,失败则回溯状态
- 增加剪枝逻辑:置0后如果行或列的和小于目标值,直接放弃该路径,不需要继续递归
- 修正
visited数组的回溯逻辑,所有路径尝试失败后会恢复单元格的未访问标记,不影响其他回溯路径
运行上述代码即可得到你给出的预期输出结果。
内容的提问来源于stack exchange,提问作者user14959162
相关产品推荐
相关产品推荐

