八数码问题(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()