Python广度优先搜索(BFS)——以“小A与小B”为例

题目链接:登录—专业IT笔试面试备考平台_牛客网

题目描述

       小A与小B这次两个人都被困在了迷宫里面的两个不同的位置,而他们希望能够迅速找到对方,然后再考虑如何逃离迷宫的事情。小A每次可以移动一个位置,而小B每次可以移动两次位置,小A移动的方向是上下左右左上左下右上右下8个方向,小B移动的方向是上下左右4个方向,请问他们最早什么时候能够找到对方,如果他们最终无法相遇,那么就输出”NO"。 

输入描述 

       第一行两个整数N,M分别表示迷宫的行和列。接下来一个N×M的矩阵其中"C"表示小A的位置,"D"表示小B的的位置,"#"表示不可通过的障碍,"."则是可以正常通过的位置。字符用空格隔开

输出描述 

       如果可以相遇,第一行输出一个YES,第二行一个整数输出最短的相遇时间。
否则就输出一个NO表示不能相遇。

示例1 

4 5
. . . . .
. # # # .
. . . # D
. . C # .

 输出

YES
3

备注:1≤n,m≤1000

思路 

        题目意思很容易理解,求两个人分别在迷宫中的某一处求最短相遇的时间。因为只用求最短相遇时间因此本题使用广度优先搜索,利用逐层扩散进行搜索。其中小A每移动一次,小B则可以移动两次,无法像传统BFS搜索中进行单目标循环搜索。首先定义一个三维数组flag来标记AB当前所在位置以及走过的路,再定义一个函数bfs(k)为每次小A或小B的移动,移动之后判断当前位置对方的flag是否为1,若为1则两人相遇,反之继续搜索。

代码 

 第一段代码:对数组进行定义和赋初值,二维数组队列peo存放小A与小B当前移动后的所有位置;三维数组flag标记AB当前所在位置以及走过的路。

n,m = map(int,input().split())
a = []
peo = [[] for _ in range(2)]
dir = [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(-1,1),(1,1)]
flag = [[[0] * m for _ in range(n)] for i in range(2)] #标记AB当前所在位置以及走过的路
for i in range(n):
    a1 = input().split()
    if 'C' in a1:
        for j in range(m):
            if a1[j] == 'C':
                peo[0].append([i,j])
                flag[0][i][j] = 1
    if 'D' in a1:
        for j in range(m):
            if a1[j] == 'D':
                peo[1].append([i,j])
                flag[1][i][j] = 1
    a.append(a1)

第二段代码:定义bfs(k)函数。因为第一段代码中peo[0]存放的是小A的初始点,peo[1]存放的是小B的初始点,因此在这里定义k = 1为小A的移动,k = 0为小B的移动。由于是逐层搜索,开头便是对当前层中的所有点进行循环,根据题目要求移动的方向是规定好的,在这里利用队列先进先出的特点通过pop()和append()函数实现。将取出的位置进行移动,将移动后位置的flag标记为1,并判断对方这个位置是否到达过也就是flag是否为1,若为1则表示两人相遇返回1,反之则返回0并继续搜索。

def bfs(k): #k = 1为A,k = 0为B
    for _ in range(len(peo[1 - k])):
        x,y = peo[1 - k].pop(0)
        for i in range(8 if k else 4):
            nx = dir[i][0] + x
            ny = dir[i][1] + y
            if 0 <= nx <= n - 1 and 0 <= ny <= m - 1 and flag[1 - k][nx][ny] == 0 and a[nx][ny] == '.':
                flag[1 - k][nx][ny] = 1
                peo[1 - k].append([nx,ny])
                flag[1 - k][nx][ny] = 1
                if flag[k][nx][ny]:
                    return 1
    return 0

第三段代码: 循环移动,输出结果。这段代码目的是为了确定移动的边界条件和输出结果。本提中结束搜索的条件则是两个人相遇或者两个人都走完所有的路之后还未相遇。两个人相遇bfs(k)返回的则是1,于是通过break跳出循环并输出结果;两个人都走完所有的路之后还未相遇bfs(k)返回的是0,此刻两个人的队列是空的,因为他们走完了所有的路还未相遇,所以结束循环,并执行ans = 0,为下面输出做准备。

ans = 0
while len(peo[0]) or len(peo[1]):
    ans += 1
    if bfs(1):
        break
    if bfs(0):
        break
    if bfs(0):
        break
else:
    ans = 0

if ans:
    print('YES')
    print(ans)
else:
    print('NO')

完整代码如下。

n,m = map(int,input().split())
a = []
peo = [[] for _ in range(2)]
dir = [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(-1,1),(1,1)]
flag = [[[0] * m for _ in range(n)] for i in range(2)] #标记AB当前所在位置以及走过的路
for i in range(n):
    a1 = input().split()
    if 'C' in a1:
        for j in range(m):
            if a1[j] == 'C':
                peo[0].append([i,j])
                flag[0][i][j] = 1
    if 'D' in a1:
        for j in range(m):
            if a1[j] == 'D':
                peo[1].append([i,j])
                flag[1][i][j] = 1
    a.append(a1)

def bfs(k): #k = 1为A,k = 0为B
    for _ in range(len(peo[1 - k])):
        x,y = peo[1 - k].pop(0)
        for i in range(8 if k else 4):
            nx = dir[i][0] + x
            ny = dir[i][1] + y
            if 0 <= nx <= n - 1 and 0 <= ny <= m - 1 and flag[1 - k][nx][ny] == 0 and a[nx][ny] == '.':
                flag[1 - k][nx][ny] = 1
                peo[1 - k].append([nx,ny])
                flag[1 - k][nx][ny] = 1
                if flag[k][nx][ny]:
                    return 1
    return 0

ans = 0
while len(peo[0]) or len(peo[1]):
    ans += 1
    if bfs(1):
        break
    if bfs(0):
        break
    if bfs(0):
        break
else:
    ans = 0

if ans:
    print('YES')
    print(ans)
else:
    print('NO')

回顾 

       之前遇到最典型的广度优先搜索则是老鼠走迷宫找出口,这题是在这个基础上的改编。本质上都是利用广度优先搜索逐层搜索的特点,一般会用于求解最短路径的问题,正好适用于本题中求解最短时间问题。与之相对应的还有深度优先搜索(DFS),DFS相对于BFS编写代码这方面简单一些, 但DFS一般通过递归实现,这会导致浪费很多时间,即使加上了记忆化搜索,还是比较费时间的。

你可能感兴趣的:(宽度优先,算法,python)