【蓝桥刷题】备战国赛——交通信号

本文介绍了2022年蓝桥杯国赛中的一道图论题,涉及如何使用Dijkstra算法求解从一点到另一点在考虑交通信号灯情况下最短时间的问题。通过构建有向图,利用类表示边的复杂信息(绿灯、红灯、黄灯时间),并结合Python的邻接表和优先队列(小根堆)实现Dijkstra算法。最后给出了完整的Python代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

蓝桥杯2022国赛真题——交通信号



🚀 每日一题,冲刺国赛 🚀

在这里插入图片描述



题目导航:

交通信号

在这里插入图片描述

🎇思路: d i j k s t r a dijkstra dijkstra

🔱思路分析:

要求从一点到另一点的最短距离,即为单源最短路径问题,用 d i j k s t r a dijkstra dijkstra算法

step

🚀1.构图:

本题描述为有向图,但其实是一个的无向图,其权值相同,只是能通过时所对应的灯的颜色不同,正向走对应绿灯时能通过,反向走对应红灯时能通过

在这里插入图片描述


而难点就在于如何构图,每条边之间不仅有权值(边长 = = == ==黄灯时间),还可能存在等待相应灯的时间,也就是说,每到一个新的结点时,在继续前往下一个结点之前,要先看红绿灯,因此,对于此类边上信息复杂的图,我们 用类表示一条边

c l a s s   E d g e : class\ Edge: class Edge:

  • self.to_red:到达红灯的时间
  • self.circle:一个周期:绿 → → → → → →
  • forward(t):表示当前结点到另一结点时正向走的,即只有绿灯时能走, t t t 表示当前时间, r e t u r n return return 到绿灯时需要等待的时间
  • back(t):表示当前结点到另一结点是反向走的,即只有红灯时能走, t t t 表示当前时间, r e t u r n return return 到红灯时需要等待的时间

class Edge:
    def __init__(self,green,red,yellow):
        self.g,self.r,self.d=green,red,yellow # 存放该边上的绿灯、红灯时间和距离
        self.to_red=self.g+self.d  # 到达红灯的时间
        # 一个循环的时间: 绿 -> 黄 -> 红 -> 黄
        self.circle=self.g+self.r+self.d+self.d # 一个周期的时间

    def forward(self,t): # 计算正向时的时间
        t%=self.circle
        return self.d+ (0 if t<self.g else self.circle-t) # 通过时间+等待时间

    def back(self,t): # 计算反向时的时间
        t=(t-self.to_red)%self.circle # 以红灯为周期起点
        return self.d+ (0 if t<self.r else self.circle-t)

再用邻接表存放这些结点和边,对于给出的点 i , j i,j i,j 来说,正向为: i → j i→j ij,反向为: j → i j→i ji

而这里涉及到了 p y t h o n python python的两个语法:

1. ∗ * 打包与解引用:

*a=tuple(map(int,input().split())):表示打包,将输入的元素打包为元组(或列表)类型,带 ∗ * 的赋值目标必须位于列表或元组中

e=Edge(*a):表示对打包的元组 a a a 进行解引用再作为参数到类中


2. 元组内存放类内方法:

P y t h o n Python Python中,方法实际上是定义在类中的函数,可以像其他函数一样存储在元组中,但是需要注意,通过元组调用方法时,需要使用对应的对象或类来调用。例如:

class MyClass:
    def my_method(self,t):
        print("Hello World"+t)

my_tuple = (MyClass().my_method,1)
my_tuple[0]('!') # 输出:Hello World!

因此,我们可以在结点 i , j i,j i,j 对应的元组中存放不同的方法,在调用时,直接按照对应的方法得到等待时间即可,对于正向 i → j i→j ij,到绿灯的时间即为等待时间,存放 f o r w a r d forward forward 方法;反向为 j → i j→i ji,到红灯的时间即为等待时间,存放 b a c k back back 方法:


n,m,start,end=map(int,input().split())
g=[[] for _ in range(n+1)] # 存图
for _ in range(m):
    i,j,*a=tuple(map(int,input().split())) # 这里*a是将剩余的传入的参数打包为元组(g,r,d)
    e=Edge(*a)  # 这里的*a是将元组a=(g,r,d)解包为g,r,d初始化一个类
    g[i].append((j,e.forward)) # 对于i来说,到结点j为正向,并传递计算时间类内方法
    g[j].append((i,e.back))    # 对于j来说,到结点i为反向


🚀2. D i j k s t r a Dijkstra Dijkstra 算法

D i j k s t r a Dijkstra Dijkstra 算法:即每一轮不断选取当前的路径最短且未被确定为最短路径的点,该点的值即为到达该点的最短路径,做上标记,并以该点去更新与之相邻且未确定最短路径的结点的值,取 m i n min min,直到所有结点都已经得到了最短路径

🎁图解算法:

  1. 先构建一张图,初始化到所有点的最短路径为 ∞ ∞

    在这里插入图片描述


  1. 假设要求点 A → F A→F AF 的最短路径,我们则以点 A A A 为源点,则到点 A A A 的最短距离为 0 0 0

    在这里插入图片描述


  1. 此时不难发现,所有点中,路径最小的点即为 A A A,则表示 A A A 已经找到了最短路径,做上标记,并更新与 A A A 相邻点 B , C B,C B,C 的最短距离

    在这里插入图片描述


  1. 更新之后,此时的最短路径对应的点为点 C C C,表示点 C C C 已经找到了最短路径,做上标记之后,更新与 C C C 相邻的未做标记的点 B , D , E B,D,E B,D,E 的最短路径

    在这里插入图片描述


  1. 重复操作,直到遍历完整张图,得到源点到 F F F 点的最短路径

    在这里插入图片描述

由于 d i j k s t r a dijkstra dijkstra 算法需要不断得到当前的最小路径对应的结点,因此我们用小根堆对当前结点进行储存

💫 p y t h o n python python中的优先队列即为小根堆,即内部的存储结构为二叉树,树的根结点保证为最小权值点,其数组的存储方式为层序存储:

在这里插入图片描述

🎇优先队列 P r i o r i t y Q u e u e PriorityQueue PriorityQueue

  • 导包: f r o m   q u e u e   i m p o r t   P r i o r i t y Q u e u e from\ queue\ import\ PriorityQueue from queue import PriorityQueue
  • 入队: q . p u t ( ) q.put() q.put()
  • 出队: q . g e t ( ) q.get() q.get()
  • 队列大小: q . q s i z e ( ) q.qsize() q.qsize()

代码实现:

from queue import PriorityQueue

def dijkstra(start):
    """
    源点:start
    到源点某结点的最短时间数组:d
    优先队列:v
    结点的被标记情况:vis
    """
    d=[float('inf') for _ in range(n+1)] # d[i]表示源点start到点i的最短时间
    d[start]=0
    vis=[True for _ in range(n+1)] # True:已找到最短路径;False:未找到最短路径
    v=PriorityQueue()  # 实例化优先队列
    v.put((0,start))   # 记录当前到结点node的最短距离 (d,node)
    while v:
        m=v.get()[1]   # 利用优先队列得到当前距里源点最近的点
        if m==end:
            break
        if vis[m]:     # 如果点m还未找到最短路径
            vis[m]=False
            for m_node,get_time in g[m]:  # 访问与结点m相邻的点
                if vis[m_node]:   # 还是先判断与m相邻的点是否被标记过,避免重复判断
                    time=d[m]+get_time(d[m])  # 得到这条路径到m_node的时间
                    if time<d[m_node]:
                        d[m_node]=time
                        v.put((d[m_node],m_node))
        else:
            continue   # 表示当前结点是之前已经找到最短距离的点

    return d[end]

d i j k s t r a dijkstra dijkstra 算法实现:

from queue import PriorityQueue

class Edge:
    def __init__(self,green,red,yellow):
        self.g,self.r,self.d=green,red,yellow # 存放该边上的绿灯、红灯时间和距离
        self.to_red=self.g+self.d  # 到达红灯的时间
        # 一个循环的时间: 绿 -> 黄 -> 红 -> 黄
        self.circle=self.g+self.r+self.d+self.d # 一个周期的时间

    def forward(self,t): # 计算正向时的时间
        t%=self.circle
        return self.d+ (0 if t<self.g else self.circle-t) # 通过时间+等待时间

    def back(self,t): # 计算反向时的时间
        t=(t-self.to_red)%self.circle # 以红灯为周期起点
        return self.d+ (0 if t<self.r else self.circle-t)


def dijkstra(start):
    """
    源点:start
    到源点某结点的最短时间数组:d
    优先队列:v
    结点的被标记情况:vis
    """
    global d,vis
    v=PriorityQueue()  # 实例化优先队列
    v.put((0,start))   # 记录当前到结点node的最短距离 (d,node)
    while v:
        m=v.get()[1]   # 利用优先队列得到当前距里源点最近的点
        if m==end:
            break
        if vis[m]:     # 如果点m还未找到最短路径
            vis[m]=False
            for m_node,get_time in g[m]:  # 访问与结点m相邻的点
                if vis[m_node]:   # 还是先判断与m相邻的点是否被标记过,避免重复判断
                    time=d[m]+get_time(d[m])  # 得到这条路径到m_node的时间
                    if time<d[m_node]:
                        d[m_node]=time
                        v.put((d[m_node],m_node))
        else:
            continue   # 表示当前结点是之前已经找到最短距离的点

    return d[end]


n,m,start,end=map(int,input().split())
g=[[] for _ in range(n+1)] # 存图
for _ in range(m):
    i,j,*a=tuple(map(int,input().split())) # 这里*a是将剩余的传入的参数打包为元组(g,r,d)
    e=Edge(*a)  # 这里的*a是将元组a=(g,r,d)解包为g,r,d初始化一个类
    g[i].append((j,e.forward)) # 对于i来说,到结点j为正向,并传递计算时间类内方法
    g[j].append((i,e.back))    # 对于j来说,到结点i为反向

d=[float('inf') for _ in range(n+1)] # d[i]表示源点start到点i的最短时间
d[start]=0
vis=[True for _ in range(n+1)] # True:已找到最短路径;False:未找到最短路径

res=dijkstra(start)
if res<float('inf'):
    print(res)
else:
    print(-1)

输出结果:

在这里插入图片描述



🎇此题为2022年 p y t h o n python python国赛研究生组的一道图论题,今天的考点是~🎇

d i j k s t r a dijkstra dijkstra 算法!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DAY Ⅰ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值