通过矩阵查找所有路线
Posted
技术标签:
【中文标题】通过矩阵查找所有路线【英文标题】:Find all routes through a matrix 【发布时间】:2019-01-14 00:09:10 【问题描述】:虽然我有一个如下矩阵,但从左上角查找所有路径,直到得到“0”。 'D' 代表底部元素是跟踪的下一步。 “R”代表正确的元素。 'B' 代表底部和右侧元素都是可选的。 'C' 代表右下角的元素。
[['R' 'C' 'D' 'B' 'D' 'C' '0']
['C' 'B' 'C' 'B' 'D' 'D' '0']
['B' 'D' 'B' 'B' 'C' 'D' '0']
['R' 'C' 'B' 'D' 'B' 'C' '0']
['B' 'B' 'R' 'C' 'B' 'D' '0']
['R' 'C' 'B' 'R' 'R' 'C' '0']
['C' 'R' 'C' 'B' 'B' 'B' '0']
['0' '0' '0' '0' '0' '0' '0']]
在这种情况下,有两条路径满足要求,分别是
(0, 0)R(0, 1)C(1, 2)C(2, 3)B(2, 4)C(3, 5)C(4, 6)0
(0, 0)R(0, 1)C(1, 2)C(2, 3)B(3, 3)D(4, 3)C(5, 4)R(5, 5)C(6, 6)0
我尝试使用递归函数来查找这两条路径中的所有“C”, 由于 (2, 3)B 中存在 fork,因此该函数仅在joint 之后完全返回其中一条路径。
(5, 5)C(4, 3)C
(3, 5)C(2, 4)C(1, 2)C(0, 1)C
那么我怎样才能修改我的代码以获得完整的结果呢?
def printC(matrix, i=0, j=0):
if matrix[i][j] == '0':
print()
return None
elif matrix[i][j] == 'B':
printC(matrix, i+1, j)
printC(matrix, i, j+1)
elif matrix[i][j] == 'D':
printC(matrix, i+1, j)
elif matrix[i][j] == 'R':
printC(matrix, i, j+1)
elif matrix[i][j] == 'C':
printC(matrix, i+1, j+1)
print((i,j), end='')
print(matrix[i][j], end='')
printC(matrix)
print()
谢谢
【问题讨论】:
是否需要递归解决方案?我的是迭代的。 【参考方案1】:解决这个问题的自然算法是backtracking:
继续前进,在所有选项中选择第一个选项 到达终点后,返回(回溯)并选择下一个选项;如果没有更多可用选项,请进一步返回 从初始元素回溯后,您已用尽所有路线。你在两种情况下“走到了尽头”:
您点击了0
=> 将当前路线添加到结果中
您碰到了一个已经在路线中的元素——即有一个循环。
根据您的规则,这是不可能的:一步总是向下、向右或两者兼而有之。所以我们不必担心这个。
要跟踪进度,我们需要:
当前坐标 整数当前路由,能够追加到末尾并从末尾弹出(即stack)。我们还需要存储最后做出的选择。
Python list
支持这个:
route = []
# go forward
route.append((new_x,new_ym,choice))
# backtrack
route.pop()
x,y,last_choice = route[-1]
找到的路线序列,可以附加到末尾
再次,list
:
results=[]
# add result
results.append(tuple(step[:2] for step in route)) # don't need choices in result
完整的程序看起来像这样(伪代码):
initialization
while True:
if can_step_forward:
make_step_with_the_next_choice
if hit_0:
add_result
else:
backtrack
if backtracked_past_the_beginning:
break
return result
使用所有这些,您现在可以将解决方案放在一起吗?
【讨论】:
你说回溯是“自然算法”,但我个人会考虑使用某种树结构,当上一个分支完成后返回下一个分支,不需要回溯。这样会更有效率。 @Graham 是的,对于一棵树,这变成了深度优先搜索。回溯更通用一点:它接受任何数量和逻辑的前进和后退步骤以及任何底层数据结构(例如,它可以以任意方式处理循环)。 啊,所以“回溯”的逻辑不一定是逐个元素检查,而是可以立即找到下一个分支的自定义回溯逻辑。 @Graham 原则上是的,虽然按照规范,它应该只是退后一步。其他可能的逻辑应该被视为对此的优化。【参考方案2】:我认为这可能更接近你想要的:
def printC(matrix, i=0, j=0, previous=None):
if not previous:
previous = []
if matrix[i][j] == '0':
print()
return None
elif matrix[i][j] == 'B':
previous_branch = previous[:]
printC(matrix, i+1, j, previous)
print(''.join(reversed(previous_branch)), end='')
printC(matrix, i, j+1, previous)
elif matrix[i][j] == 'D':
printC(matrix, i+1, j, previous)
elif matrix[i][j] == 'R':
printC(matrix, i, j+1, previous)
elif matrix[i][j] == 'C':
previous.append(''.format((i,j), matrix[i][j]))
printC(matrix, i+1, j+1, previous)
print((i,j), end='')
print(matrix[i][j], end='')
诀窍是存储以前的命令,然后在遍历另一个分支时重新启动它们。之所以必须反转它们,是因为您在递归中选择的回溯方法会在采用路径后打印命令,这意味着首先打印最新的命令,因为它首先被解析,等等。
不幸的是,由于递归方法,代码有点复杂;在这种情况下,我实际上认为迭代方法更适合。
编辑:这是一种迭代方法,但我必须注意嵌套列表可能不是最好的树替代:
def printC(matrix, i=0, j=0):
trace_root = [(i, j)]
current_trace = trace_root
trace_waiting = []
while True:
if matrix[i][j] == '0':
if trace_waiting:
current_trace = trace_waiting.pop()
i, j = current_trace[-1]
else:
break
elif matrix[i][j] == 'B':
current_trace.extend([[(i+1, j)], [(i, j+1)]])
trace_waiting.append(current_trace[-1])
current_trace = current_trace[-2]
i, j = current_trace[-1]
else
if matrix[i][j] == 'D':
i += 1
elif matrix[i][j] == 'R':
j += 1
elif matrix[i][j] == 'C':
i, j = i+1, j+1
current_trace.append((i,j))
return trace_root
【讨论】:
前一个代码实现有一个小问题,但是方法还是挺有用的,前一个和后一个,谢谢! @YuqS 在什么情况下会失败?以上是关于通过矩阵查找所有路线的主要内容,如果未能解决你的问题,请参考以下文章