2020-12-30

95 篇文章 2 订阅

继续修炼——从一种状态到另一种状态的最短路径

问题:有两行数据,第一行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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值