继续修炼——从一种状态到另一种状态的最短路径
问题:有两行数据,第一行3个,第二行5个,分别为1-5和3个0。数据交换的规则是:各行两相邻数据之一为0的(另一个不为0)可以相互交换。上下两行之间只能交换中间位置(位置1和位置5)的数据,并且两个数据之一为0(另一个不为0)。
例如:
4 5 1
0 2 3 0 0
移动一步可以转换到
4 5 1
0 2 0 3 0
然后再移动一步转换到
4 0 1
0 2 5 3 0
找出从一种状态转换到另一种状态的最短路径,并且显示路径。应有无解提示。
这个问题与八数码问题有相似的地方,但远没有八数码那么复杂。采用宽搜就可以解决问题。
程序代码如下:
import copy
import time
base = [[0, 1], [1, 2], [1, 5], [3, 4], [4, 5], [5, 6], [6, 7]] # 可交换数据的位置信息
source = [3, 4, 0, 0, 1, 0, 2, 5]
target = [4, 2, 1, 0, 5, 0, 0, 3]
# target = [0, 0, 0, 1, 2, 3, 4, 5]
# target = [5, 4, 3, 2, 1, 0, 0, 0]
father = [source]
gather = [] # 保存扩展集合,每个数据包括父状态和子状态集合
result = [] # 保存最终路径
sun = [True] # 保存某个层次所有状态扩展后的集合,赋值保证主程序能够进入while循环
# 对某个状态进行扩展
def spread(state):
temp = []
for b in base:
n, m = b[0], b[1]
p = copy.deepcopy(state)
if bool(state[n]) ^ bool(state[m]): # 把数字转换成逻辑值后进行异或运算
p[n], p[m] = p[m], p[n] # 交换数据
if p == target:
gather.append([state, [target]])
show()
mark = 0
for ga in gather: # 去重
if p in ga[1]:
mark = 1
break
if mark == 0:
temp.append(p)
sun.append(p)
gather.append([state, temp])
# 逆向搜根获取最终路径并显示
def show():
global target
while 1:
for g in range(len(gather)):
if target in gather[g][1]:
result.append(target)
target = gather[g][0]
gather.remove(gather[g])
if target == source:
result.append(source)
result.reverse()
for index, r in enumerate(result):
print("第{}步:".format(index))
for i in range(8):
if i == 0:
print(" ", end="")
if i == 3:
print()
print(r[i], end=" ")
print()
print("花费{}秒。".format(time.time() - t0))
exit()
break
if __name__ == '__main__':
t0 = time.time()
if source == target:
print("成功!起始状态与目标状态一致,无须移动。")
exit()
while sun:
sun = []
for fa in father:
spread(fa)
father = sun
print("无解!")
运行结果:
D:\Python\Python38\python.exe D:/Python/study/20201230.py
第0步:
3 4 0
0 1 0 2 5
第1步:
3 0 4
0 1 0 2 5
第2步:
3 0 4
0 0 1 2 5
第3步:
3 1 4
0 0 0 2 5
第4步:
3 1 4
0 0 2 0 5
第5步:
3 1 4
0 2 0 0 5
第6步:
3 0 4
0 2 1 0 5
第7步:
0 3 4
0 2 1 0 5
第8步:
0 3 4
2 0 1 0 5
第9步:
0 3 4
2 0 0 1 5
第10步:
0 0 4
2 0 3 1 5
第11步:
0 4 0
2 0 3 1 5
第12步:
4 0 0
2 0 3 1 5
第13步:
4 0 0
2 3 0 1 5
第14步:
4 0 0
2 3 1 0 5
第15步:
4 1 0
2 3 0 0 5
第16步:
4 0 1
2 3 0 0 5
第17步:
4 0 1
2 3 0 5 0
第18步:
4 0 1
2 3 5 0 0
第19步:
4 5 1
2 3 0 0 0
第20步:
4 5 1
2 0 3 0 0
第21步:
4 5 1
0 2 3 0 0
第22步:
4 5 1
0 2 0 3 0
第23步:
4 0 1
0 2 5 3 0
第24步:
4 0 1
0 2 5 0 3
第25步:
4 0 1
0 2 0 5 3
第26步:
4 0 1
0 0 2 5 3
第27步:
4 2 1
0 0 0 5 3
第28步:
4 2 1
0 0 5 0 3
第29步:
4 2 1
0 5 0 0 3
花费4.79681134223938秒。
Process finished with exit code 0