广度优先搜索-八数码问题

八数码问题(Eight):八数码问题是人工智能中的经典问题
有一个3*3的棋盘,其中有0-8共9个数字,0表示空格,
其他的数字可以和0交换位置。求由初始状态
到达目标状态
8 2 3 1 2 3
1 4 6 ----> 4 5 6
5 7 0 7 8 0
的步数最少的解。

题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。
棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。
要求解的问题是:给出一种初始布局(初始状态)和目标布局
(为了使题目简单,设目标状态为123456780),找到一种最少步骤的移动方法,
实现从初始布局到目标布局的转变。

输入格式:输入初始状态,一行九个数字,空格用0表示
123406758
823146570
输出格式:只有一行,该行只有一个数字,表示从初始状态到目标状态需要的
最少移动次数(测试数据中无特殊无法到达目标状态数据)
2
18
https://www.cnblogs.com/czc1999/p/10428066.html
https://blog.csdn.net/yangysc/article/details/50710439

python代码实现:
  1 # 目标状态
  2 goal_status = 123456780
  3 # 四种移动方向
  4 moves = ["u", "d", "r", "l"]
  5 myQueue = [0 for i in range(400000)]  # 状态队列,状态总数362880,对每种状态进行插入或者移除
  6 qHead = 0  # 队头指针
  7 qTail = 1  # 队尾指针
  8 
  9 
 10 class Node:
 11     status = 0  # 状态
 12     father = 0  # 父节点指针,即myQueue的下标
 13     move = ""  # 父节点到本节点的移动方式 u/d/r/l
 14 
 15     def __init__(self, s, f, m):
 16         self.status = s
 17         self.father = f
 18         self.move = m
 19 
 20 
 21 # 求从status经过move移动后得到的新状态。若移动不可行则返回-1
 22 def  NewStatus(status, move):
 23     zeroPos = 0  # 字符'0'的位置
 24     # 不足9位时,前面补0
 25     tmp = str(status).zfill(9)
 26     zeroPos = tmp.find('0')
 27     temp_list = list(tmp)
 28     if move == "u":
 29         if zeroPos - 3 < 0:  # 空格在第一行
 30             return -1
 31         else:
 32             temp_list[zeroPos] = temp_list[zeroPos - 3]
 33             temp_list[zeroPos - 3] = '0'
 34     elif move == "d":
 35         if zeroPos + 3 > 8:  # 空格在第三行
 36             return -1
 37         else:
 38             temp_list[zeroPos] = temp_list[zeroPos + 3]
 39             temp_list[zeroPos + 3] = '0'
 40     elif move == "l":
 41         if zeroPos % 3 == 0:  # 空格在第一列
 42             return -1
 43         else:
 44             temp_list[zeroPos] = temp_list[zeroPos-1]
 45             temp_list[zeroPos-1] = '0'
 46     elif move == "r":
 47         if zeroPos % 3 == 2:  # 空格在第三列
 48             return -1
 49         else:
 50             temp_list[zeroPos] = temp_list[zeroPos+1]
 51             temp_list[zeroPos+1] = '0'
 52     tmp = "".join(temp_list)
 53     return int(tmp)
 54 
 55 
 56 def bfs(status):
 57     global qHead, qTail, goal_status, moves
 58     # 把每个状态都存入该set中,set有判重的功能
 59     expanded = set()
 60     # 初始状态放入队头
 61     myQueue[qHead] = Node(status, -1, 0)
 62     # 把初始状态存入set集合
 63     expanded.add(status)
 64     # 队列不为空
 65     while qHead != qTail:
 66         status = myQueue[qHead].status
 67         if status == goal_status:  # 找到目标状态
 68             return True
 69         for i in range(4):  # 尝试4种移动方向
 70             # 根据moves[i]列表的方向移动,产生一个新的数
 71             newStatus = NewStatus(status, moves[i])
 72             # 不可移,试下一个方向
 73             if newStatus == -1:
 74                 continue
 75             # 已扩展过,试下一个方向
 76             if newStatus in expanded:
 77                 continue
 78             # 把新方向的状态整数值加入到队列中
 79             expanded.add(newStatus)
 80             # 按广度优先遍历的思路理解的话,qHead应该理解为层
 81             # 也就是新加入的这个节点是从哪层的某个状态转变过来的
 82             myQueue[qTail] = Node(newStatus, qHead, moves[i])
 83             qTail += 1
 84 
 85         qHead += 1
 86 
 87     return False
 88 
 89 
 90 def main():
 91     global qHead, myQueue
 92     result = [0 for i in range(400000)]  # 要输出的移动方案
 93     start_status = int(input())
 94     if bfs(start_status):
 95         n = 0
 96         pos = qHead
 97         # pos = 0 说明已经回退到初始状态了
 98         while pos != 0:
 99             # 通过father找到成功的状态序列,输出相应步骤
100             # 从后往前找
101             # kk = myQueue[pos].status
102             result[n] = myQueue[pos].move
103             pos = myQueue[pos].father
104             n += 1
105         print("共需要移动%d步" % n)
106         print("具体的移动步骤如下:")
107         for i in range(n-1, -1, -1):
108             print(result[i])
109     else:
110         print("不能转换到该状态!")
111 
112     return 0
113 
114 
115 if __name__ == '__main__':
116     main()

 

 
posted @ 2020-07-25 09:34  StudyNLP  阅读(1146)  评论(0编辑  收藏  举报