【Leetcode】815. 公交路线——1964

题目

815. 公交路线

给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。

例如,路线 routes[0] = [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... 这样的车站路线行驶。
现在从 source 车站出发(初始时不在公交车上),要前往 target 车站。 期间仅可乘坐公交车。

求出 最少乘坐的公交车数量 。如果不可能到达终点车站,返回 -1 。

  • \(1\leq routes.length \leq 500\)
  • \(1\leq routes[i].length \leq 10^5\)
  • \(sum(routes[i].length)<=10^5\)

思路

根据题目的描述,首先可以想到的就是这个题目似乎与找树得最短路径也就是Dijkstra算法有关,唯一不同的是,其加入了车站

也就是说对于最基本得最短路径算法而言,其变为了一个十分稠密的图,每一个routes[i]都可以表示其之间的任意节点都是互相连接的,这样造成的结果就是最终的图会十分的稠密。此时再采用最基本的Dijksta算法的时间复杂度就是\(O(n^2)\)

正确的思路

我们就从题目本身的场景入手,不进行抽象。

如果现在就是我们需要从当前位置去到我们的目的地,那么此时选择的公交线路一定是先乘坐某条线路到达某个站,之后再次乘坐某个线路到达某个站,最终到达目的地。

其中任何的两个所停留的站点之间的某条公交线路一定不会是同一个公交线路。

如果存在乘坐两个相同的公交线路的两段,那么为什么我们不从第一个这个公交线路直达到第二个公交线路的下车点呢?

另外,我们所停留的站点不会存在重复的站点

如果我们两个时间段都在某个相同的站点,等于我们乘车做了一个圈,完全是没有必要的,因为我们的目的是转乘更少的线路

据此,我们得到了两个解题的关键

  1. 不会乘坐同一个公交线路两次
  2. 不会达到同一个站点两次

因此我们对最基本的Dijksta进行改造,到达某个节点的时候,我们可以选择其可以乘坐的所有路线,到达下一个站点。当然,如果路线之前已经乘坐过,那么此时就没有必要再当前节点再次乘坐车辆了。

同样地,对于节点而言, 如果之前通过某途径到达了,那么此时就没有必要再次到达此地方了。

class Solution:
    def numBusesToDestination(self, routes: List[List[int]], source: int, target: int) -> int:
        g = defaultdict(list)
        for i,line in enumerate(routes):
            for x in line:
                g[x].append(i)
        
        had_line = set()
        had_sta = set()
        had_sta.add(source)
        q = [[0, source]]
        while q:
            length, sta = heappop(q)
            if sta==target:return length
            for line in g[sta]:
                if line in had_line:continue
                for x in routes[line]:
                    if x in had_sta:continue
                    heappush(q, (length+1, x))
                    had_sta.add(x)
                had_line.add(line)
        return -1

此外,可以发现的是,由于题目中节点之间的距离都可以认为是相同的,即如果相连接,那么就是乘坐一个路线接口到达,花费相同的代价。因此上述的Dijkstra也可以进行改进,将使用的改造为一个队列。先加入进去的一定是花费代价较小的,后加入的花费代价较大。

class Solution:
    def numBusesToDestination(self, routes: List[List[int]], source: int, target: int) -> int:
        g = defaultdict(list)
        for i,line in enumerate(routes):
            for x in line:
                g[x].append(i)
        
        had_line = set()
        had_sta = set()
        had_sta.add(source)
        q = deque([(0, source)])
        while q:
            length, sta = q.popleft()
            if sta==target:return length
            for line in g[sta]:
                if line in had_line:continue
                for x in routes[line]:
                    if x in had_sta:continue
                    q.append((length+1, x))
                    had_sta.add(x)
                had_line.add(line)
        return -1
posted @ 2024-09-17 22:46  TICSMC  阅读(13)  评论(0)    收藏  举报