电动车游城市

标签: 最短路 堆(优先队列)

难度: Hard

小明的电动车电量充满时可行驶距离为 `cnt`,每行驶 1 单位距离消耗 1 单位电量,且花费 1 单位时间。小明想选择电动车作为代步工具。地图上共有 N 个景点,景点编号为 0 ~ N-1。他将地图信息以 `[城市 A 编号,城市 B 编号,两城市间距离]` 格式整理在在二维数组 `paths`,表示城市 A、B 间存在双向通路。初始状态,电动车电量为 0。每个城市都设有充电桩,`charge[i]` 表示第 i 个城市每充 1 单位电量需要花费的单位时间。请返回小明最少需要花费多少单位时间从起点城市 `start` 抵达终点城市 `end`。 **示例 1:** >输入:`paths = [[1,3,3],[3,2,1],[2,1,3],[0,1,4],[3,0,5]], cnt = 6, start = 1, end = 0, charge = [2,10,4,1]` > >输出:`43` > >解释:最佳路线为:1->3->0。 >在城市 1 仅充 3 单位电至城市 3,然后在城市 3 充 5 单位电,行驶至城市 5。 >充电用时共 3\*10 + 5\*1= 35 >行驶用时 3 + 5 = 8,此时总用时最短 43。 ![image.png](https://pic.leetcode-cn.com/1616125304-mzVxIV-image.png) **示例 2:** >输入:`paths = [[0,4,2],[4,3,5],[3,0,5],[0,1,5],[3,2,4],[1,2,8]], cnt = 8, start = 0, end = 2, charge = [4,1,1,3,2]` > >输出:`38` > >解释:最佳路线为:0->4->3->2。 >城市 0 充电 2 单位,行驶至城市 4 充电 8 单位,行驶至城市 3 充电 1 单位,最终行驶至城市 2。 >充电用时 4\*2+2\*8+3\*1 = 27 >行驶用时 2+5+4 = 11,总用时最短 38。 **提示:** - `1 <= paths.length <= 200` - `paths[i].length == 3` - `2 <= charge.length == n <= 100` - `0 <= path[i][0],path[i][1],start,end < n` - `1 <= cnt <= 100` - `1 <= path[i][2] <= cnt` - `1 <= charge[i] <= 100` - 题目保证所有城市相互可以到达

Submission

运行时间: 69 ms

内存: 16.8 MB

class Solution:
    def electricCarPlan(self, paths: List[List[int]], cnt: int, start: int, end: int, charge: List[int]) -> int:
        n = len(charge)
        dist = [[inf] * (cnt+1) for _ in range(n)]

        dist[start][0] = 0
        
        adj = defaultdict(list)
        for u,v,w in paths:
            adj[u].append((v,w))
            adj[v].append((u,w))
        q = []
        heappush(q, (0, start, 0))
        while q:
            t, u, c = heappop(q)
            if t > dist[u][c]:
                continue
            if u==end:
                return t
            
            if c<cnt:
                nt = t + charge[u]
                if nt < dist[u][c+1]:
                    dist[u][c+1] = nt
                    heappush(q, (nt, u, c+1))
            for v, w in adj[u]:
                if c >= w and t+w < dist[v][c-w]:
                    dist[v][c-w] = t + w
                    heappush(q, (t+w, v, c-w))
    
        

Explain

这个题解使用了带有优先队列(最小堆)的Dijkstra算法变种,结合状态转移来解决问题。每个状态由当前城市和车上的电量决定,形成一个状态空间。dist数组存储到达每个状态的最小时间。从起点开始,不断探索出发到其他城市的可能性,同时考虑在当前城市充电的可能性。当从优先队列中取出的状态的时间大于已记录的最小时间,则跳过该状态。算法结束的条件是当访问到终点城市时,队列中的第一个合法状态即为最小时间。此外,算法还考虑了每次充电的时间成本,并在每个城市尝试充电后再行驶到下一个城市,以及直接从当前电量出发到能达到的下一个城市。

时间复杂度: O(n^2 * cnt * log(n*cnt))

空间复杂度: O(n * cnt)

class Solution:
    def electricCarPlan(self, paths: List[List[int]], cnt: int, start: int, end: int, charge: List[int]) -> int:
        n = len(charge)
        dist = [[inf] * (cnt+1) for _ in range(n)]

        dist[start][0] = 0
        
        adj = defaultdict(list)
        for u,v,w in paths:
            adj[u].append((v,w))
            adj[v].append((u,w))
        q = []
        heappush(q, (0, start, 0))
        while q:
            t, u, c = heappop(q)
            if t > dist[u][c]:
                continue
            if u==end:
                return t
            
            if c<cnt:
                nt = t + charge[u]
                if nt < dist[u][c+1]:
                    dist[u][c+1] = nt
                    heappush(q, (nt, u, c+1))
            for v, w in adj[u]:
                if c >= w and t+w < dist[v][c-w]:
                    dist[v][c-w] = t + w
                    heappush(q, (t+w, v, c-w))
    

Explore

在设计电动车游城市的算法中,每次仅考虑增加1单位电量而非多个单位的原因有两个:首先,这种策略简化了状态转移的复杂性。在每个城市充电时只考虑增加1单位电量,可以使状态空间和决策过程更加清晰和易于管理。其次,这种方法可以保证在任何时刻都能找到达到某状态的最小时间。如果一次充多个单位电量,虽然可能在某些情况下更快,但会使问题的状态空间剧增,增加了计算的复杂度。此外,这种逐步充电的策略可以在每一步都重新评估是否继续充电或驶向下一个城市,这样做提供了更好的灵活性和可能达到更优的总体策略。因此,虽然看似增加了步骤,实际上此策略有助于维护算法的效率和实现全局最优解。