• 图的遍历应用之拯救007,python版本详细解析


    题目


    给定一张图,007在图的一个顶点(岛)上,如果想要逃生就要通过跳到鳄鱼的头上,最终跳到岸上逃生。

    设鳄鱼池是长宽为100米的方形,中心坐标为 (0, 0),且东北角坐标为 (50, 50)。池心岛是以 (0, 0) 为圆心、直径15米的圆。

    给定池中分布的鳄鱼的坐标、以及007一次能跳跃的最大距离,判断007是否能够逃生。

    分析:

    其实就是图的遍历问题,一直递归查找,直到找到边缘。与普通遍历不同的是顶点之间是否有边需要自行计算。

    整个程序抽象出几个方法:

    • createGraph 构建有N个顶点的图
    • insertEdge 判断两个顶点之间是否有边,有边的条件是,两点之间的距离是否在007的跳跃范围之内
    • dfs或bfs算法

    开始实现(伪代码,如需完整代码请留言)

    第一步,根据题意,我们可以提取出几个要素来设计数据对象:

            顶点对象,至少具有2个属性值,分别是横坐标和纵坐标,因此可以得下列数据结构

    1. class GNode():
    2. """
    3. 图顶点
    4. """
    5. def __init__(self, x=None, y=None):
    6. # 图的每个节点
    7. self.x = x
    8. self.y = y

           边对象,至少需要3个属性值,起始顶点和终止顶点,边的距离,边是否有向等

    1. class ENode(ABC):
    2. """
    3. """
    4. DIRECTED = None
    5. def __init__(self):
    6. self.weight = None # 权重
    7. self.v1 = None # 顶点1
    8. self.v2 = None # 顶点2

            有N个顶点的图,至少需要一个数据结构存储每个顶点的坐标值,顶点之间的连通关系,因此可以得出下列数据结构(矩阵实现)

    1. class Graph():
    2. """
    3. """
    4. def __init__(self):
    5. # 邻接矩阵,存储顶点之间边的关系
    6. # 如G[0][1]=-1 表示顶点0和顶点1之间不连同
    7. # 如G[0][1]=8 表示顶点0和顶点1之间连同,距离为8
    8. self.G = [[]]
    9. # 存储顶点数据集,每个元素是顶点GNode实例
    10. self.data = []

            游戏,至少包含一张图,小岛的位置,小岛半径,007的跳跃距离

    1. class GameSave007():
    2. """
    3. 拯救007游戏
    4. """
    5. def __init__():
    6. self.graph = None # 存储图
    7. self.islandPos = None # 小岛起始位置
    8. self.islandRadius None # 小岛的半径
    9. self.jumpDist = None # 007跳跃的距离

    第二步,我们需要创建游戏图,包括几个数据操作:创建空图、创建边、插入边等

            首先我们需要创建具有N个顶点的,没有边的图,在Graph对象中加一个创建空图操作。

    1. class Graph():
    2. ... # 省略
    3. def createGraph(self, vertexs:list):
    4. """
    5. 初始化一个没有边的图
    6. vertexNum 顶点集,每个元素是一个顶点
    7. """
    8. # 由于没有边,所有位置初始化为-1
    9. self.G = [[-1 for i in range(vertexNum)] for j in range(vertexNum)]
    10. self.data = [ v for v in vertexs] # 存储顶点的坐标
    11. return self

            其次填充边,找到图所有的边,在Graph加找边操作、插入边、判断两个顶点是否存在边等操作

    1. class Graph():
    2. ... # 省略
    3. def insertEdge(self, e: ENode):
    4. """
    5. 插入一条边
    6. """
    7. self.G[e.v1][e.v2] = e.weight # 默认V1指向V2
    8. if not e.DIRECTED: # 如果是无向图,还需要v2指向v1
    9. self.G[e.v2][e.v1] = e.weight
    10. def isEdge(self, v1, v2, jumpDist) -> int:
    11. """
    12. 判断两个顶点是否有边
    13. -1 没有边,
    14. >0 权重
    15. """
    16. v1 = self.data[v1] # 获取顶点的坐标值
    17. v2 = self.data[v2]
    18. xdist = abs(v2.x - v1.x)
    19. ydist = abs(v2.y - v1.y)
    20. if (xdist*xdist + ydist*ydist <= (jumpDist*jumpDist)):
    21. return int(math.sqrt(xdist*xdist + ydist*ydist))
    22. return -1
    23. def findEdge(self, jumpDist):
    24. """
    25. 找出图的所有边
    26. """
    27. for i, val in enumerate(self.G):
    28. for j, val in enumerate(self.G[i][i+1:]):
    29. j = i+1+j
    30. weight = self.isEdge(i, j, jumpDist)
    31. if weight == -1:
    32. continue
    33. e = UnDirENode()
    34. e.v1 = i
    35. e.v2 = j
    36. e.weight = weight
    37. self.insertEdge(e)

    第三步,写核心图遍历算法(DFS 深度优先搜索)

            如果找到一个顶点,可以直接连通“岸边”,那么007就得救了。连通岸边的条件是 x+=jumpDist大于50或者x+=10大于50。

    1. class Save007DFSVisit(IVisitGraph):
    2. def isSave(self, g, v) -> bool:
    3. """
    4. 判断当前顶点v能否跳到岸上,step=1
    5. g:图对象
    6. v: 顶点下标
    7. """
    8. # 分析 岸边顶点为 (-50,?) (?, 50) (50, ?) (?, -50)
    9. v = g.data[v]
    10. if ((abs(v.x)+g.jumpDist)>=50 or (abs(v.y)+g.jumpDist>=50)):
    11. # 成功脱逃
    12. return True
    13. return False
    14. def visit(self, g:GNode, v: int):
    15. """
    16. DFS 深度优先搜索
    17. g:图对象
    18. v: 顶点下标
    19. """
    20. self.visited[v] = True # 跳到这个鳄鱼上面
    21. ans = "NO"
    22. if self.isSave(g, v):
    23. # 判断当前鳄鱼头能否直接跳到岸上
    24. ans = "YES"
    25. else:
    26. # 不能跳到岸上,继续找下一个鳄鱼头
    27. for i, val in enumerate(g.G[v]):
    28. if (g.G[v][i]>0 and not self.visited[i]): # 判断是否还有其他连通的、未访问的鳄鱼头
    29. ans = self.visit(g, i)
    30. if ans == "YES":
    31. break
    32. return ans

    最后一步:从小岛出发,遍历所有在跳跃范围内的鳄鱼头,对这个鳄鱼头进行DFS,直到找到连通图。

    1. if abs(g.jumpDist+self.IslandDir) >= 50:
    2. print("拯救007成功")
    3. exit()
    4. for i, val in enumerate(g.data): # 遍历所有顶点,直到找到连通图
    5. if ((not v.visited[i]) and self.firstJump(g, i)):
    6. ans = c.doVisit(v, g, i)
    7. if ans == 'YES':
    8. break
    9. print("回到起点")
    10. if ans == "YES":
    11. print("拯救007成功")
    12. else:
    13. print("拯救007失败,呜呜呜")
    1. def firstJump(self, g, v) -> bool:
    2. """
    3. 第一跳:确定是否能从孤岛上跳到v
    4. """
    5. # 当前起点(0,0)
    6. v = g.data[v]
    7. r = self.jumpDist+self.islandRadius
    8. if ((r*r) >= (v.x*v.x + v.y*v.y)):
    9. return True
    10. return False

    最后总结:

            整个程序非常简单,也非常有趣。

            先设置游戏参数,如小岛起始位置,小岛半径,007的跳跃距离等;

            然后创建图,鳄鱼,关键点是边的查找;

            最后,从岛上开始逃生,关键操作是图的遍历;

            通过这个小游戏,我们可以很好的锻炼了图的遍历,甚至你可以提高难度,找出最短逃生路径,也就是图的最短路径问题。

           

  • 相关阅读:
    使用JAVA开发情侣飞行棋小程序系统的优势与注意事项
    云计算环境中需要用到的新技术
    Oracle 处理json数据
    基于51单片机的八路电压表采集Proteus仿真
    WEBRTC中的Candidate是指什么
    30天Python入门(第二十九天:深入了解Python API的使用2.0)
    C++入门基础04:代码实战经验分享(全局变量与局部变量重名、静态变量、数据类型选取、养成变量创建就初始化、少用多层指针)
    嵌入式音频软件开发之协议时序图分析方法
    ovn metadata (by quqi99)
    互联网Java工程师面试题·Memcached篇·第一弹
  • 原文地址:https://blog.csdn.net/weixin_40647516/article/details/126178693