1.递归法:先放程序:
# 迷宫求解:分析和设计】
'''
问题分析:
问题表示:
迷宫本身使用一个元素值为0/1的矩阵表示。迷宫入口、出口可以用一对下标表示。
A: 为了防止程序在某些局部兜圈子,必须采用某种方法记录已经探查过的位置:两种方法(1)采用专门的结构记录这种信息
(2)把已经探查过的标记在 迷宫上(将采用这种方式--把已经探查过的位置,对应矩阵元素标记为2,计算中发现位置值为2
就不在探索了)(元素非0就是不能通过)。
B: 确定当前可行位置方向的技术。位置(i,j)有4个相邻位置[(i,j+1),(i+1,j),(i,j-1),(i-1,j)],
定义一个二元组的表,如dirs=[(0,1),(1,0),(0,-1),(-1,0)],可以通过将其与(i,j)相加得到四邻的位置。
'''
# 12*14
maze=[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],]
dirs = [(0,1),(1,0),(0,-1),(-1,0)] # 位置(i,j)计算出四邻位置需要加的数对(东>南>西>北)
def mark(maze,pos): # 给迷宫maze的位置pos标2,表示已经探查过的位置
maze[pos[0]][pos[1]] = 2
def passable(maze,pos): # 检查迷宫maze的位置pos是否可行,可行返回True
return maze[pos[0]][pos[1]] == 0
# 迷宫的递归求解】
'''
算法过程:
1. mark当前位置(标记当前访问位置)
2. 检查当前位置是否为出口,如果是则成功结束
3. 逐个检查当前位置的4邻是否可以通达出口(递归调用自身)
4. 如果对4邻的探索都失败,报告失败。
'''
def find_path(maze,pos,end):
mark(maze,pos)
if pos == end: # 基线条件--已经达到出口
print(pos,end='') # 输出这个位置
return True # 成功结束
for i in range(4): # 否则按照4个方向顺序探查
nextp = pos[0]+dirs[i][0],pos[1]+dirs[i][1]
# 考虑下一个可能方向
if passable(maze,nextp): # 不可行的相邻位置不算
if find_path(maze,nextp,end):
print(pos,end='')
return True
return False
print(np.shape(maze))
print("迷宫:", find_path(maze,pos=(1,1),end=(10,12)))
for i in range(12):
print(maze[i])
'''
# 运行结果
(12, 14)
(10, 12)(9, 12)(8, 12)(7, 12)(6, 12)(5, 12)(5, 11)(5, 10)(6, 10)(6, 9)(6, 8)(6, 7)(6, 6)(6, 5)(6, 4)(6, 3)(7, 3)(7, 2)(7, 1)(6, 1)(5, 1)(4, 1)(3, 1)(2, 1)(1, 1)迷宫: True
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 2, 2, 2, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1]
[1, 2, 1, 2, 2, 2, 2, 1, 2, 1, 0, 1, 0, 1]
[1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 0, 1, 0, 1]
[1, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 1]
[1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1]
[1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1]
[1, 2, 2, 2, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1]
[1, 0, 1, 2, 1, 2, 1, 0, 1, 0, 1, 0, 2, 1]
[1, 0, 1, 2, 1, 2, 1, 0, 1, 1, 1, 1, 2, 1]
[1, 0, 1, 2, 2, 2, 1, 0, 0, 1, 0, 0, 2, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
'''
2.以一个简单的例子来分析递归过程,如下图是一个5*5的迷宫地图(1表示非通道,0表示通道):
1 | 0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 1 |
1 | 0 | 1 | 0 | 1 |
0 | 0 | 1 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
(S、N、W、E)分别为南、北、西、东四个方向,f_p(5)代表递归函数,5*5的迷宫递归简要过程如下图所示:
3.回溯法:这种算法在工作中执行两种基本动作:前进和后退
前进:
条件:若当前位置存在尚未探查的四邻位置。则继续前进。
操作:选择下一个位置并向前探查。若还存在其他未探查的分支,就记录相关信息(记录当前位置,以及未探查分支的相对位置),以便将来使用。如果找到出口,则成功结束。
后退(回溯):
条件:若遇到死路,不存在尚未探查的四邻位置。则回溯。
操作:找回最近记录的那个位置信息,检查是否还存在未探索的分支,若有就取一个,未探索的位置作为当前位置并前进;若没有并将其删除并继续回溯。
若已穷尽所有可能:不能找到出口时以失败结束。
4.代码如下:
# 栈和回溯法解决迷宫问题
def maze_solve(maze,start,end):
if start == end: # 若开始位置即为结束位置时,打印位置信息并返回
print(start)
return
st = sStack() # 申请一个栈对象,栈的实现请查看https://blog.csdn.net/weixin_39781462/article/details/82290886
mark(maze,start) # 标记开始位置
st.push((start,0)) # 开始位置入栈
while not st.is_empty(): # 直到栈内元素为空才出循环
pos,nxt = st.pop() # 取出栈中元素
for i in range(nxt,4): # 取
nextp = (pos[0]+dirs[i][0], # 下一个要处理的位置
pos[1]+dirs[i][1])
if passable(maze, nextp): # 位置元素满足通过要求
st.push((pos,i+1)) # 位置入栈--存入栈的是序对(pos.nxt),其中分支点位置用pos(行列坐标对)表示,nxt是正数表示回溯到该位置后需要探索的下一位置方向
mark(maze,nextp) # 标记位置
st.push((nextp,0)) # 位置入栈
if nextp == end: # 位置为end时,输出栈内元素,即为寻找的路径
while not st.is_empty():
print(st.pop()[0], end='')
return
break # 退出内层循环,下次迭代将以新栈顶为当前位置继续
print("No path found") # 找不到路径
maze=[[1, 0, 1, 1, 1],
[1, 0, 0, 0, 1],
[1, 0, 1, 0, 1],
[0, 0, 1, 0, 1],
[1, 1, 1, 1, 1]]
print("回溯法解迷宫问题:")
maze_solve(maze,start=(0,1),end=(3,0))
'''
回溯法解迷宫问题:
(3, 0)(3, 1)(2, 1)(1, 1)(0, 1)
'''
5.题目描述:给定一个M*M的二维数组,每个值1的元素 代表一个团队,如果两个团队的上下或左右方向相邻,说明两个团队有紧密合作的关系,可以形成一个部门,没有合作关系的团队放到不同的部门,判定给定输入中有多少个部门?
例如,给定一个二维数组:
1 | 0 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 1 |
0 | 0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 | 0 |
其中有九个团队,其中有3个部门(已经使用不同的颜色进行标记)。
解题思路:符合一个部门的条件是:可以相串连的一些团队可以称为一个部门,因此我们可以将问题转化为迷宫房间位置的个数统计(位置为1代表房间,位置为0代表墙壁),每个房间是由一个或多个1组成,一个房间的查找我们可以使用【迷宫搜索路线的思想--使用栈和回溯】,将收到的房间位置标记为2,然后再进行下一个房间的搜索,统计搜索次数记为所求。
使用栈和回溯求解:
#----------------------------------对maze矩阵预先处理,添加0边--------------------------------------------------------
maze=[ [0, 1, 0, 0, 1],
[1, 1, 1, 0, 1],
[0, 1, 0, 0, 0],
[1, 0, 0, 1, 0],
[0, 1, 1, 1, 1] ]
def maze_add2(maze): # 为原矩阵maze添加一圈0元素
maze2 = np.zeros((len(maze)+2,len(maze)+2),dtype=np.int32)
for i in range(1,len(maze)+1):
for j in range(1,len(maze)+1):
maze2[i][j] = maze[i-1][j-1]
return maze2
maze1 = maze_add2(maze)
#-----------------------------使用栈和回溯方法进行上下左右区域块标记为2-----------------------------------------------
dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 位置(i,j)计算出四邻位置需要加的数对(东>南>西>北)
def mark(maze, pos): # 给maze的位置pos标2,表示已经探查过的位置
maze[pos[0]][pos[1]] = 2
def passable(maze, pos): # 检查maze的位置pos是否可行,可行返回True
return maze[pos[0]][pos[1]] == 1
# 使用栈和回溯法标记位以start为开始的连接区域块
def maze_solve(maze,start):
st = sStack() # 申请一个栈对象,栈的实现请查看https://blog.csdn.net/weixin_39781462/article/details/82290886
mark(maze,start) # 标记开始位置
st.push((start,0)) # 开始位置入栈
while not st.is_empty(): # 直到栈内元素为空才出循环
pos,nxt = st.pop() # 取出栈中元素
for i in range(nxt,4): #
nextp = (pos[0]+dirs[i][0], # 下一个要处理的位置
pos[1]+dirs[i][1])
if passable(maze, nextp): # 位置元素满足通过要求
st.push((pos,i+1)) # 位置入栈--存入栈的是序对(pos.nxt),其中分支点位置用pos(行列坐标对)表示,nxt是正数表示回溯到该位置后需要探索的下一位置方向
mark(maze,nextp) # 标记位置
st.push((nextp,0)) # 位置入栈
break # 退出内层循环,下次迭代将以新栈顶为当前位置继续
print("marked the value of the start'department") # 找不到路径
# 为矩阵添加一圈0元素
for i in range(len(maze1)):
print(maze1[i])
#------------------------------------------遍历矩阵中每一个元素-------------------------------------------------------
num=0
for i in range(len(maze1)):
for j in range(len(maze1)):
if maze1[i][j] == 1:
maze_solve(maze1, start=(i, j))
print("\n")
num +=1
print("\n")
for i in range(len(maze1)):
print(maze1[i])
print("maze中部门的数量为:",num)
'''
# 输出
[0 0 0 0 0 0 0]
[0 0 1 0 0 1 0]
[0 1 1 1 0 1 0]
[0 0 1 0 0 0 0]
[0 1 0 0 1 0 0]
[0 0 1 1 1 1 0]
[0 0 0 0 0 0 0]
marked the value of the start'department
marked the value of the start'department
marked the value of the start'department
marked the value of the start'department
[0 0 0 0 0 0 0]
[0 0 2 0 0 2 0]
[0 2 2 2 0 2 0]
[0 0 2 0 0 0 0]
[0 2 0 0 2 0 0]
[0 0 2 2 2 2 0]
[0 0 0 0 0 0 0]
maze中部门的数量为: 4
'''