问题描述
在河的左岸有3个传教士、1条船和3个食人者,传教士们想用这条船将所有的成员运到河的右岸,但是受到以下条件的限制:
(1)船每次最多只能装2个乘客(传教士和食人者都会划船)。
(2)在任何岸边,如果食人者数目超过传教士则传教士将被食人者吃掉。
(3)假定食人者会服从任何一种过河安排。编写深度优先搜索算法程序,找出一个确保全部成员安全过河的解。
思路分析
采用列表status= []# 表示方式[ML, CL, MR, CR, B](左传教士、左食人者、右传教士、右食人者、船)来记录一次操作后的状态,参考魏宇轩大佬的思路,(链接https://kangyanfeng.blog.csdn.net/article/details/84404168)每次操作只有五种可能即0,1 1,0 2,0 0,2 1,1五种动作,也用列表去存储,这样每次的操作就可以用列表元素相加减。而状态点使用字典存放,但是列表不能作为python的键值,所以转化成元组放进字典。之后的算法思路也和魏宇轩大佬的思路相同,但是使用python实现。
代码
g = {} # 存放父子节点
statusl = []# 表示方式[ML, CL, MR, CR, B] 左传教士、左野人、右传教士、右野人、船
route = [] # 一次路径
way = [] # 总路径
actions = [[1, 0], [0, 1], [1, 1], [0, 2], [2, 0]] #五种操作方式
def result(s):
if s[0] < 0 or s[1] < 0 or s[2] < 0 or s[3] < 0: # 负数
return
if (s[0] < s[1] and s[0] != 0) or (s[2] < s[3] and s[2] != 0): # 传教士被吃
return
d = tuple(s) #元组作为状态点
if len(statusl) > 1:
f = tuple(statusl[-2][:])
if f in g.keys() and d not in g[f]:
g[f].append(d)
else:
g[f] = [d]
for k in statusl[:-1]: #重复状态
if k[0] == s[0] and k[1] == s[1] and k[3] == s[3] and k[4] == s[4]:
return
mid = [0] * 5
for j in actions:
mid[0] = s[0] - j[0] * s[4]
mid[1] = s[1] - j[1] * s[4]
mid[2] = s[2] + j[0] * s[4]
mid[3] = s[3] + j[1] * s[4]
mid[4] = -s[4]
statusl.append(mid[:])
result(mid) #判断该动作是否合理,并加入字典
statusl.pop()
return
# 深度搜索寻找路径
def dfs(s):
s = tuple(s) #字典的值是列表里面包含元组,原因是字典的键值不能是列表
if s in route: #已经在路径里面
route.append(s)
return
# 到达终点,记录路径
if s == (0, 0, 3, 3, -1):
route.append(s)
way.append(route[:])
return
route.append(s)
for i in range(len(g[s])):
dfs(g[s][i]) #递归搜索直到到达终点或者重复
route.pop()
def main():
start = [3, 3, 0, 0, 1]
statusl.append(start)
result(start)
dfs(start)
num = 0 # 统计次数
# 输出路径
for k in way:
num += 1
print("第%d条路径:" % num)
print("左传教士,左食人者,右传教士,右食人者, 船 ")
for i in k:
print("{} {} {} {} {} ".format(i[0], i[1], i[2], i[3], i[4]))
print("总共有%d条路径" % num)
if __name__ == '__main__':
main()