• 算法竞赛进阶指南:噩梦(Python)


    题目描述:

    给定一张 N×M 的地图,地图中有 1 个男孩,1 个女孩和 2 个鬼。

    字符 . 表示道路,字符 X 表示墙,字符 M 表示男孩的位置,字符 G 表示女孩的位置,字符 Z 表示鬼的位置。

    男孩每秒可以移动 3 个单位距离,女孩每秒可以移动 1 个单位距离,男孩和女孩只能朝上下左右四个方向移动。

    每个鬼占据的区域每秒可以向四周扩张 2 个单位距离,并且无视墙的阻挡,也就是在第 k 秒后所有与鬼的曼哈顿距离不超过 2k 的位置都会被鬼占领。

    注意: 每一秒鬼会先扩展,扩展完毕后男孩和女孩才可以移动。

    求在不进入鬼的占领区的前提下,男孩和女孩能否会合,若能会合,求出最短会合时间。

    输入格式

    第一行包含整数 T,表示共有 T 组测试用例。

    每组测试用例第一行包含两个整数 N 和 M,表示地图的尺寸。

    接下来 N 行每行 M 个字符,用来描绘整张地图的状况。(注意:地图中一定有且仅有 1 个男孩,1 个女孩和 2 个鬼)

    输出格式

    每个测试用例输出一个整数 S,表示最短会合时间。

    如果无法会合则输出 −1。

    每个结果占一行。

    数据范围

    1<n,m<800

    输入样例:

    1. 3
    2. 5 6
    3. XXXXXX
    4. XZ..ZX
    5. XXXXXX
    6. M.G...
    7. ......
    8. 5 6
    9. XXXXXX
    10. XZZ..X
    11. XXXXXX
    12. M.....
    13. ..G...
    14. 10 10
    15. ..........
    16. ..X.......
    17. ..M.X...X.
    18. X.........
    19. .X..X.X.X.
    20. .........X
    21. ..XX....X.
    22. X....G...X
    23. ...ZX.X...
    24. ...Z..X..X

    输出样例:

    1. 1
    2. 1
    3. -1

    题目分析: 

    拿到题目后首先思考用什么算法:本题特征很明显 是一个地图类型的题 自然而然想到使用bfs求解

    但我们发现本题最棘手的地方是男生,女生和鬼每一轮都需要扩展,这该怎么办呢,我们进一步发现,其实鬼的扩展并不需要展示出来,只需要男女生每次扩展时,检测一下当前点是否是在鬼的扩展范围之内即可

    扩展男生女生就较为简单了 只需要正常先扩展一个再扩展另一个即可 每当男生/女生扩展到一个格子 都要判断这个格子能不能走 如果能走 看看是否被女生/男生扩展过 若是扩展过说明他们可以相遇 可以直接返回答案了。否则标记为男生/女生走过该点

    具体步骤如下 :

    我们在bfs中按时间t从 1 开始枚举。
    如果男孩和女孩都不能再继续扩展,则跳出枚举。
    对于男孩,每次扩展三步,并标记扩展到的格子。
    如果某个能扩展的格子被女孩扩展过,那么直接返回现在的时间。
    对于女孩,每次扩展一步,并标记扩展到的格子。
    如果某个能扩展的格子被男孩扩展过,那么直接返回现在的时间。
    对于鬼,由于鬼是无视墙的,所以我们只需要在扩展男孩和女孩的时候,判断下有没有进入鬼的占领范围即可。
    题目中已经给出了,在第 k 秒后所有与鬼的曼哈顿距离不超过 2k 的位置都会被鬼占领
    我们在第 t 秒扩展的时候,判断被扩展的格子是否与某个鬼的曼哈顿距离小于 2t 即可

    上代码! 

    1. def check(x,y,step) : # 检查该点能不能走到
    2. if x<0 or x>=N or y<0 or y>=M : return False
    3. if Map[x][y] == 'X' : return False
    4. if abs(x-ghost[0][0]) + abs(y-ghost[0][1]) <= step * 2 : return False
    5. if abs(x-ghost[1][0]) + abs(y-ghost[1][1]) <= step * 2 : return False
    6. return True
    7. def bfs() :
    8. qM = [(Mi,Mj)] ; qG = [(Gi,Gj)]
    9. st = [[0 for i in range(M)]for j in range(N)] # 记录男生/女生走过的情况
    10. st[Mi][Mj] = 1 ; st[Gi][Gj] = 2 # 记得把初始点放在st中
    11. step = 0
    12. while qM or qG : # 扩展男生
    13. step += 1 # 时间+1
    14. for n in range(3) : # 注意男生可以一次最多走三步
    15. lenM = len(qM)
    16. for _ in range(lenM) :
    17. if qM == [] : break
    18. nx,ny = qM.pop(0)
    19. if not check(nx,ny,step) : continue
    20. for i,j in [(1,0),(0,1),(-1,0),(0,-1)] :
    21. px = nx + i ; py = ny + j
    22. if not check(px,py,step) : continue
    23. if st[px][py] == 2 : return step # 如果被女生走到过
    24. if not st[px][py] : st[px][py] = 1 ; qM.append((px,py))
    25. for n in range(1) :
    26. lenG = len(qG) # 扩展女生
    27. for _ in range(lenG) :
    28. if qG == [] : break
    29. nx,ny = qG.pop(0)
    30. if not check(nx,ny,step) : continue
    31. for i,j in [(1,0),(0,1),(-1,0),(0,-1)] :
    32. px = nx + i ; py = ny + j
    33. if not check(px,py,step) : continue
    34. if st[px][py] == 1 : return step # 如果被男生走到过
    35. if not st[px][py] : st[px][py] = 2 ; qG.append((px,py))
    36. return -1
    37. T = int(input())
    38. for _ in range(T) :
    39. N,M = map(int,input().split())
    40. Map = []
    41. for i in range(N) : Map.append(list(input()))
    42. ghost = []
    43. for i in range(N) :
    44. for j in range(M) :
    45. if Map[i][j] == 'M' : Mi,Mj = i,j
    46. elif Map[i][j] == 'G' : Gi,Gj = i,j
    47. elif Map[i][j] == 'Z' : ghost.append((i,j))
    48. print(bfs())

  • 相关阅读:
    从入门到精通,收下这 22 个 Python 学习网站
    为 Serverless Devs 插上 Terraform 的翅膀,解耦代码和基础设施,实现企业级多环境部署(下)
    数据产品读书笔记——数据产品经理和其他角色的关系
    QT连接OpenCV库实现人脸识别
    字符输入转换流&字符输出转换流
    倒计时第3天!Google Summer of Code报名即将截止!(Casbin社区还有空缺名额)
    Linux shell编程学习笔记12:布尔运算和逻辑运算
    大型监控网络设备架构
    排序-算法
    C/C++程序,从命令行传入参数
  • 原文地址:https://blog.csdn.net/m0_54689021/article/details/125536779