【专题】搜索题型(BFS+DFS)
【专题】搜索题型(BFS+DFS)
一、回溯法
回溯算法都可以抽象成一个树状结构,可以将其抽象成一个【n叉树问题】。如果满足递归的条件,树枝可以无限增加,直到找到所需要数据为止;如果不满足,树枝则会折断。树的深度取决于要搜索问题的层数,树的宽度取决于每个节点处理集合的大小。
排列问题:N个数按一定规则全排列,有几种排列方式
组合问题:N个数里面按一定规则找出k个数的集合
子集问题:一个N个数的集合里有多少符合条件的子集
1. 回溯模版——排列问题
核心点:排列与顺序有关

主旨展现:
排列要求数字不重复——每次选择的数字需要打标记——vis数组
要输出当前排列——记录路径——path数组。
回溯:先打标记,记录路径、然后下一层,回到上一层,清除标记。
题目描述:
给定一个整数 m,将数字 1∼m 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
n=int(input())
# 标记数组:表示数字是否被用过
vis = [0] * (n+1)
# 存储当前路径(排列结果)
li=[]
def dfs(depth):
# depth:第depth个数字
if depth == n:
print(li)
return
# 遍历每一种可能(边界到n为止)
for i in range(1,n+1):
if vis[i] == 0:
li.append(i)
vis[i]=1 # 标志为1,表示已经用过
dfs(depth+1) # 进入下一层
vis[i]=0 # 回溯:撤销选择(标志位0)
li.pop() # 回溯:撤销选择(移除数据)
dfs(0)
--------------------------------------
输入:3
输出:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
--------------------------------------
类似题目:素数环【环】
输入正整数 n,把整数 1,2,…,n 组成一个环,使得相邻两个整数之和均为素数。
输入格式:
输入正整数 n。
输出格式:
输出所有满足条件的环。
import math
n = int(input())
# 存储当前路径(组合结果)
li = []
# 标记数组:表示数字是否被用过
vis=[0]*(n+1)
# 标志
flag=0
# 函数:判断是否为质数,是则返回1,不是则返回0
def prime(n):
# 质数从2开始
if n<=1:
return 0
sqrt = int(math.sqrt(n))
for i in range(2,sqrt+1):
if n%i==0:
return 0
return 1
# 组合模型
def dfs(depth):
# 初始化
global flag
flag=0
# 找到一组组合方式
if depth==n:
# 对于环而言,先判断首项和尾部
if prime(li[0] + li[-1])==1:
# 满足条件,则对从头开始进行判断
for i in range(1,len(li)):
if prime(li[i]+li[i-1])==1:
flag=1
else:
flag=0
break
if flag==1:
for i in range(len(li)):
if i<len(li)-1:
print(li[i],end=' ')
else:
print(li[i])
return
for i in range(1,n+1):
if vis[i]==0:
li.append(i)
vis[i]=1
dfs(depth+1)
vis[i]=0
li.pop()
dfs(0) # 初始调用:深度0,起始值1
----------------------------------------
输入:6
输出:
1 4 3 2 5 6
1 6 5 2 3 4
2 3 4 1 6 5
2 5 6 1 4 3
3 2 5 6 1 4
3 4 1 6 5 2
4 1 6 5 2 3
4 3 2 5 6 1
5 2 3 4 1 6
5 6 1 4 3 2
6 1 4 3 2 5
6 5 2 3 4 1
----------------------------------------
2. 回溯模版——组合问题
核心点:组合与顺序无关
题目描述:
排列与组合是常用的数学方法,其中组合就是从 n 个元素中抽出 r 个元素(不分顺序且 r≤n),我们可以简单地将 n 个元素理解为自然数 1,2,…,n,从中任取 r 个数。
现要求你输出所有组合。
例如 n=5,r=3,所有组合为:
123,124,125,134,135,145,234,235,245,345。
输入格式:
一行两个自然数 n,r(1<n<21,0≤r≤n)。
输出格式:
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。
n,k=list(map(int,input().split()))
# 存储当前路径(组合结果)
li = []
# 标记数组:表示数字是否被用过
vis=[0]*(n+1)
def dfs(depth,val):
# 终止条件:已选k个元素
if depth == k:
for i in li:
print('{:>3}'.format(i), end='')
print()
return
# 进行讲解,该思想能用到哪些类似的题目中
for i in range(val,n+1):
if vis[i]==0:
li.append(i)
vis[i]=1
dfs(depth+1,i+1) # 下一层只能选比i大的元素
vis[i]=0 # 回溯:撤销选择(标志位0)
li.pop() # 回溯:撤销选择(移除数据)
dfs(0,1) # 初始调用:深度0,起始值1
-------------------------------------------
输入:5 3
输出:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
-------------------------------------------
组合问题
给定两个正整数n、k,假设序列S=[1,2,3,…,n],求从S中任选k个的所有可能结果【组合】。
n,size=list(map(int,input().split()))
# 全局变量result来存储所有找到的组合
result=[]
# 临时列表temp来存储当前正在构建的组合
# index表示当前索引
def dfs(index,temp):
# 如果当前组合的长度等于size,将其加入结果中
if len(temp)==size:
result.append(temp[:]) # 【重点】
return
# 如果当前索引超过了n,返回
if index>n:
return
# 选择当前索引,继续递归
temp.append(index)
dfs(index+1,temp)
# 不选择当前索引,继续递归
temp.pop()
dfs(index+1,temp)
dfs(1,[])
for element in result:
print(" ".join(map(str,element)))
-------------------------------------------
输入:
4 2
输出:
1 2
1 3
1 4
2 3
2 4
3 4
-------------------------------------------
3. 回溯模版——子集问题
子集的问题相对于组合和排列用的更多,子集的模版特别简单直接看代码
n=int(input())
li = list(map(int,input().split()))
# lis 存放结果
lis=[]
def dfs(depth):
if depth==n:
print(lis)
return
# 选择第depth个元素
lis.append(li[depth])
dfs(depth+1)
lis.pop()
# 不选择第depth个元素
dfs(depth+1)
dfs(0)
-------------------------------------
输入:
3
1 2 3
输出:
[1, 2, 3]
[1, 2]
[1, 3]
[1]
[2, 3]
[2]
[3]
[]
-------------------------------------
类似题目:调配料
题目描述:
Perket 是一种流行的美食。为了做好 Perket,厨师必须谨慎选择食材,以在保持传统风味的同时尽可能获得最全面的味道。你有 n 种可支配的配料。对于每一种配料,我们知道它们各自的酸度 s 和苦度 b。当我们添加配料时,总的酸度为每一种配料的酸度总乘积;总的苦度为每一种配料的苦度的总和。
众所周知,美食应该做到口感适中,所以我们希望选取配料,以使得酸度和苦度的绝对差最小。
另外,我们必须添加至少一种配料,因为没有任何食物以水为配料的。
输入格式:
第一行一个整数 n,表示可供选用的食材种类数。
接下来 n 行,每行 2 个整数 s**i 和 b**i,表示第 i 种食材的酸度和苦度。
输出格式:
一行一个整数,表示可能的总酸度和总苦度的最小绝对差。
n=int(input())
a=[]
b=[]
for i in range(n):
x,y=list(map(int,input().split()))
a.append(x)
b.append(y)
# 初始化:记录 酸程度 和 苦程度
suan=1
ku=0
def bfs(depth):
global suan
global ku
global mins
for i in range(depth,n):
suan=suan*a[i]
ku=ku+b[i]
mins=min(mins,abs(suan-ku))
bfs(i+1)
suan=suan/a[i]
ku=ku-b[i]
mins=abs(a[0]-b[0])
bfs(0)
print(int(mins))
--------------------------------------
输入:
4
1 7
2 6
3 8
4 9
输出:
1
--------------------------------------
二、棋盘问题
1、N皇后问题
在n×n的国际棋盘上摆放n个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
def NQueen(n):
def backtrack(row):
# 内部的函数需要修改外部函数的变量时,就可以使用nonlocal关键字来指明这个变量(不是全局变量)
nonlocal counter # 记录成功放置N个皇后的方案总数
# 退出条件(遍历到第n行):说明所有行都成功放置了皇后,此时计数器counter加一。
if row == n:
counter += 1
return
for col in range(n):
if (col not in columns) and (row-col not in diagonals1) and (row+col not in diagonals2):
# 将当前位置添加进来
columns.add(col)
diagonals1.add(row-col)
diagonals2.add(row+col)
# 回溯:如果可以,则标记该列为已占用,并递归到下一行。
backtrack(row + 1)
# 用于尝试列的下一个位置
columns.remove(col)
diagonals1.remove(row-col)
diagonals2.remove(row+col)
counter = 0
columns = set() # 存储列位置
diagonals1 = set() # 存储主对角线: row - col
diagonals2 = set() # 存储辅对角线:row + col
backtrack(0)
return counter
n = int(input())
print(NQueen(n))
-----------------------
输入:8
输出:92
-----------------------
2、八皇后问题
在8×8的国际棋盘上摆放了8个皇后,判断其是否是一个合法的摆放方式,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
def f(board):
rows=set() #存储行位置
cols = set() # 存储列位置
main_diag=set() #存储主对角线: row - col
anti_diag=set() #存储辅对角线:row + col
for row in range(8):
for col in range(8):
# 判断若是皇后
if board[row][col] == 1:
if (row in rows) or (col in cols) or ((row-col) in main_diag) or ((row+col) in anti_diag):
return 0
rows.add(row)
cols.add(col)
main_diag.add(row-col)
anti_diag.add(row+col)
return 1
board = [list(map(int,input().split())) for _ in range(8)]
# 输出
if f(board)==0:
print("NO")
else:
print("YES")
-------------------------
输入:
0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0
0 0 1 0 0 0 0 0
输出:
YES
-------------------------
三、联通块问题
1、小怂爱水洼
问题描述:
小怂喜欢收集水洼中的水,他每到一个水量不为零的小水洼中就会收集里面的所有水。
小怂去到了一个大小为 N×M 的水洼上,水洼上的每一块小水洼水量为 ai,j(i∈[1,n],j∈[1,m])。小怂的起始点是水洼中水量不为 0 的任意一个小水洼。假设小怂的起始点是 (1,1),他可以移动无数次,每次移动只能移动到当前水洼上下左右四个方向的相邻小水洼上,并且需要满足相邻小水洼水量大于 0,即如果新的小水洼水量为零,小怂就不能走到这个小水洼上。特别地,小怂可以重复走到某块小水洼,但是小水洼中的水只能被收集一次;如果起始点的水洼中有水,他会收集那些水。
值得注意的是:每块上下左右相连且水量不为 0 的小水洼会合成一块大水洼,小怂每到一块新的大水洼,他之前收集到的水量会变为 0。
求解小怂在大水洼中可以收集到的最大水量。
思路讲解:
1 2 0 3 4 0 0 0 5
第一个大水洼由 (1,1), (1,2), (2,1), (2,2) 四个小水洼组成,水量总和为 10。
第二个大水洼由 (3,3) 一个小水洼组成,水量总和为 5。
因此小怂在大水洼中可以获得的最大水量是 10。
n,m=list(map(int,input().split()))
pool = [list(map(int, input().split())) for _ in range(n)]
# 记录是否走过:0表示没有走过;1表示走过
visit = [[0] * m for _ in range(n)]
# 位移偏量:上下左右
direction = [[0,1],[0,-1],[1,0],[-1,0]]
ans=0
def bfs(sx, sy):
water = 0 # 初始化当前连通区域的水量为0
queue = [[sx, sy]] # 创建一个【队列】,用于BFS,初始时将起始格子加入队列
while queue: # 当队列不为空时,继续搜索
x, y = queue.pop(0) # 弹出队列的第一个元素(如:sx,sy),获取其坐标【当前正在访问的格子】
water += pool[x][y] # 将当前格子的水量加入到water中
for dx, dy in direction: # 遍历四个方向
xx, yy = x + dx, y + dy # 计算新坐标
if xx < n and xx>=0 and yy < m and yy>=0: # 判断新坐标是否在网格范围内
# 如果新坐标对应的格子未被访问过且水量不为0
if visit[xx][yy]==0 and pool[xx][yy] != 0:
visit[xx][yy] = 1 # 标记为已访问
queue.append([xx, yy]) # 将新坐标加入队列,继续搜索
return water # 返回当前连通区域的水量
for i in range(n):
for j in range(m):
# 若这个格子没有做过 并且 对应值不为0
if visit[i][j]==0 and pool[i][j] != 0:
visit[i][j]=1
ans=max(ans,bfs(i,j))
print(ans)
------------------------------------------
输入:
3 3
1 2 0
3 4 0
0 0 5
输出:
10
------------------------------------------
2、全球变暖
问题描述:
你有一张某海域 N×N 像素的照片,.
表示海洋、 #
表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中 “上下左右” 四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
解题思路:
n = int(input())
a=[list(input()) for _ in range(n)]
# 记录是否走过:0表示没有走过;1表示走过
visited = [[0]*n for _ in range(n)]
# 位移偏量:上下左右
direction = [[0,1],[0,-1],[1,0],[-1,0]]
# 记录岛屿的数量
count=0
def dfs(x,y):
global flag # 表示是否会被淹掉:0表示会被淹,1表示不会被淹
ans=0 # 岛屿格子数
queue = [[x, y]] # 创建一个【队列】,用于BFS,初始时将起始格子加入队列
while queue:
x, y = queue.pop(0) # 弹出队列的第一个元素(如:sx,sy),获取其坐标【当前正在访问的格子】
# 【当前正在访问的格子】不会被淹没
if a[x - 1][y] == '#' and a[x + 1][y] == '#' and a[x][y - 1] == '#' and a[x][y + 1] == '#':
flag = 1
for dx, dy in direction: # 遍历四个方向
xx, yy = x + dx, y + dy # 计算新坐标
if a[xx][yy]=='#' and visited[xx][yy]==0: # 判断新坐标是否在网格范围内
visited[xx][yy] = 1 # 标记为已访问
queue.append([xx, yy]) # 将新坐标加入队列,继续搜索
for i in range(n):
for j in range(n):
if visited[i][j] == 0 and a[i][j] == '#':
visited[i][j]=1
flag=0
dfs(i,j)
if flag==0: # 被淹掉
count+=1
print(count)
---------------------------------------------
输入:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输入:
1 # 被淹掉的岛屿数量为1
---------------------------------------------
3、填涂染色
问题描述:
由数字 0 组成的方阵中,有一任意形状的由数字 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2。例如:6×6 的方阵(n=6),涂色前和涂色后的方阵如下:
如果从某个 0 出发,只向上下左右 4 个方向移动且仅经过其他 0 的情况下,无法到达方阵的边界,就认为这个 0 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内的 0 是连通的(两两之间可以相互到达)。
解题思路:从边缘DFS,把圈外的0都记录下【visited=1】,最后没记录的就是圈内的0
n=int(input())
grid=[list(map(int,input().split())) for _ in range(n)]
# 记录圈外的0
visited = [[0]*n for _ in range(n)]
# 位移偏量:上下左右
direction = [[0,1],[0,-1],[1,0],[-1,0]]
# 标记圈外0
def dfs(x,y):
queue=[[x,y]]
while queue:
x1,y1=queue.pop(0)
# 遇到边界1,停止标记
if grid[x1][y1]==1:
return
for dx,dy in direction:
xx,yy=x1+dx,y1+dy
# 判断新坐标是否在网格范围内
if xx>=0 and xx<n and yy>=0 and yy<n:
# 格子是0【可通过】 并且 没有访问过
if grid[xx][yy] == 0 and visited[xx][yy]==0:
visited[xx][yy]=1
queue.append([xx, yy]) # 将新坐标加入队列,继续搜索
# 从矩阵的四个边界上的每一个位置开始进行搜索
for i in range(n):
dfs(0, i)
dfs(n-1, i)
dfs(i, 0)
dfs(i, n-1)
for i in range(n):
for j in range(n):
# 若不是圈外 并且 访问的是0
if visited[i][j]==0 and grid[i][j]==0:
grid[i][j]=2
for i in range(n):
for j in range(n):
if j<n-1:
print(grid[i][j],end=' ')
else:
print(grid[i][j])
------------------------------------------------
输入:
6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
输出:
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1
------------------------------------------------
4、岛屿数量
题目描述:给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。
例如下图所示,含有3个岛屿数量。
n, m = map(int, input().split())
# 邻接矩阵【二维数组】
grid=[list(map(int,input().split())) for _ in range(n)]
# 位移偏量:上下左右
direction = [[0, 1], [1, 0], [0, -1], [-1, 0]]
# 记录是否走过:0表示没有走过;1表示走过
visited = [[0] * m for _ in range(n)]
num=0
def bfs(x,y):
queue=[[x,y]]
while queue:
x1,y1=queue.pop(0)
if grid[x1][y1]==0:
return
for dx,dy in direction:
xx=x1+dx
yy=y1+dy
# 判断新坐标是否在网格范围内
if xx < n and xx>=0 and yy < m and yy>=0:
# 如果下一个位置是未访问过的陆地,则标记为已访问,并继续深度优先搜索
if grid[xx][yy]==1 and visited[xx][yy]==0:
visited[xx][yy]=1
queue.append([xx,yy])
for i in range(n):
for j in range(m):
if visited[i][j]==0 and grid[i][j]==1:
num += 1 # 发现新的陆地区域,计数器加1
bfs(i,j)
print(num)
-----------------------------------
输入:
4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1
输出:
3
-----------------------------------
四、搜索问题
1、扫雷游戏
扫雷游戏是一款十分经典的单机小游戏。
在 n 行 m 列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。
玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。
游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。
现在给出 n 行 m 列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。
注:一个格子的周围格子包括其:左上、上、右上、左、右、左下、下、右下八个方向上与之直接相邻的格子。
基本思想:
![]()
n,m=list(map(int,input().split()))
# 存放信息
g=[input() for _ in range(n)]
# 位移偏量
dx = [-1, -1, -1, 0, 0, 1, 1, 1]
dy = [-1, 0, 1, -1, 1, -1, 0, 1]
def check(x,y):
cnt=0
# 遍历八个方向(位移偏量)
for i in range(0,8):
a=x+dx[i] # 各个方向上对应的x
b=y+dy[i] #各个方向上对应的 y
# 边界性
if a<0 or a>n-1 or b<0 or b>m-1:
continue
if g[a][b]=='*':
cnt+=1
return cnt
# 遍历每一个点,搜索附近的地雷数
for i in range(0,n):
for j in range(0,m):
if g[i][j]=='*':
print('*',end='')
else:
ans=check(i,j)
print(ans,end='')
if i<n-1:
print()
-----------------------------------
输入:
3 3
*??
???
?*?
输出:
*10
221
1*1
-----------------------------------
2、走迷宫
题目描述:
给定一个 N×M 的网格迷宫G。G的每个格子要么是道路,要么是障碍物(道路用 1 表示,障碍物用 0 表示)。
已知迷宫的入口位置为 (x1,y1),出口位置为 (x2,y2)。问从入口走到出口,最少要走多少个格子。
输入描述:
输入第 1 行包含两个正整数 N,M,分别表示迷宫的大小。
接下来输入一个 N×M 的矩阵。若 Gi,j=1表示其为道路,否则表示其为障碍物。
最后一行输入四个整数 x1,y1,x2,y2,表示入口的位置和出口的位置。
输出描述:
输出仅一行,包含一个整数表示答案。
若无法从入口到出口,则输出 −1。
n,m=map(int,input().split())
a=[list(map(int,input().split())) for i in range(n)]
x1,y1,x2,y2=list(map(int,input().split()))
# 从 0 开始的索引,因此将这些坐标减去 1 以转换为索引
x1, y1, x2, y2 = x1 - 1, y1 - 1, x2 - 1, y2 - 1
# 记录是否走过:0表示没有走过;1表示走过
visit = [[0] * m for _ in range(n)]
# 位移偏量:上下左右
direction = [[0,1],[0,-1],[1,0],[-1,0]]
def bfs():
queue = [[x1, y1, 0]]
while queue:
x,y,z=queue.pop(0)
# 到达目的地
if x==x2 and y==y2:
return z
# 遍历四个方向
for dx,dy in direction:
# 计算新坐标
xx = x + dx
yy = y + dy
# 判断新坐标是否在网格范围内 并且 新坐标为道路(值为1) 并且 标志是未走过的(值为0)
if xx < n and xx >= 0 and yy < m and yy >= 0 and a[xx][yy]==1 and visit[xx][yy]==0:
visit[xx][yy]=1 # 已经访问了,标记为1
queue.append([xx,yy,z+1])
return -1
print(bfs())
-------------------------------------------
输入:
5 5
1 0 1 1 0
1 1 0 1 1
0 1 0 1 1
1 1 1 1 1
1 0 0 0 1
1 1 5 5
输出:
8
-------------------------------------------
3、马的遍历
题目描述:
有一个 n×m 的棋盘,在某个点 (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输入格式:
输入只有一行四个整数,分别为 n,m,x,y。
输出格式:
一个 n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 −1)。
n,m,x,y=list(map(int,input().split()))
# 从 0 开始的索引,因此将这些坐标减去 1 以转换为索引
x,y=x-1,y-1
# 棋盘
board = [[-1]*m for _ in range(n)]
board[x][y]=0 # 【起始位置】设置为0
# 记录是否走过:0表示没有走过;1表示走过
visit = [[0] * m for _ in range(n)]
visit[x][y] = 1 # 【标记起点】为已访问
# 位移偏量:马走日字格
direction = [[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]]
def bfs(x1,y1):
queue=[[x1,y1,0]]
while queue:
x, y, z = queue.pop(0)
for dx,dy in direction:
xx=x+dx
yy=y+dy
if xx < n and xx >= 0 and yy < m and yy >= 0 and visit[xx][yy] == 0:
visit[xx][yy] = 1 # 标记为已访问
if board[xx][yy] == -1 or board[xx][yy] > z + 1: # 更新最短步数
board[xx][yy] = z + 1
queue.append([xx, yy, z + 1]) # 加入队列继续搜索
return -1
bfs(x, y) # 从起点开始搜索
# 输出结果矩阵
for i in range(n):
for j in range(m):
print(board[i][j], end=' ')
print() # 换行输出每一行的结果
--------------------------------------------------
输入:
3 3 1 1
输出:
0 3 2
3 -1 1
2 1 4
--------------------------------------------------
4、蜂巢
蜂巢由大量的六边形拼接而成, 定义蜂巢中的方向为: 0 表示正西方向, 1 表示西偏北 60度,2 表示东偏北 60度,3 表示正东, 4 表示东偏南 60度,5 表示西 偏南 60度。
对于给定的一点 O, 我们以 O 为原点定义坐标系, 如果一个点 A 由 O 点 先向 d 方向走 p 步再向 (d+2) mod 6 方向 ( d 的顺时针 120度 方向) 走 q 步到达, 则这个点的坐标定义为 (d,p,q)。在蜂窝中, 一个点的坐标可能有多种。
下图给出了点 B(0,5,3) 和点 C(2,3,2) 的示意。
给定点 (d1,p1,q1) 和点 (d2,p2,q2), 请问他们之间最少走多少步可以到达?
输入格式:
输入一行包含 6 个整数 d1,p1,q1,d2,p2,q2 表示两个点的坐标, 相邻两个整 数之间使用一个空格分隔。
输出格式:
输出一行包含一个整数表示两点之间最少走多少步可以到达。
d1, p1, q1, d2, p2, q2 = map(int, input().split())
x = [-1, -0.5, 0.5, 1, 0.5, -0.5] #0-5 六个方向移动单位长度,分别对应的x轴变化情况
y = [0, 1, 1, 0, -1, -1] #0-5 六个方向移动单位长度,分别对应的y轴变化情况
#计算第一个点(x1, y2)对应原点o的坐标
x1 = x[d1]*p1 + x[(d1+2)%6]*q1
y1 = y[d1]*p1 + y[(d1+2)%6]*q1
#计算第二个点(x2, y2)对应原点o的坐标
x2 = x[d2]*p2 + x[(d2+2)%6]*q2
y2 = y[d2]*p2 + y[(d2+2)%6]*q2
#计算两个点在x轴上和y轴上的差值的绝对值
disX, disy = abs(x2-x1), abs(y2-y1)
#情况1 第一个点直接可以斜着到达第二个点
if disy*0.5 >= disX:
print(int(disy))
#情况2 第一个点必须分别沿x轴和y轴(先x或先y不影响结果)各走一段,才能到第二个点
else:
# print(disy+disX-0.5*disy)
print(int(0.5 * disy + disX))
------------------------------------------------------
输入:0 5 3 2 3 2
输出:7
------------------------------------------------------