383 观光(单源最短路径扩展-次短路的求解)

1. 问题描述:

"您的个人假期"旅行社组织了一次比荷卢经济联盟的巴士之旅。比荷卢经济联盟有很多公交线路。每天公共汽车都会从一座城市开往另一座城市。沿途汽车可能会在一些城市(零或更多)停靠。旅行社计划旅途从 S 城市出发,到 F 城市结束。由于不同旅客的景点偏好不同,所以为了迎合更多旅客,旅行社将为客户提供多种不同线路。游客可以选择的行进路线有所限制,要么满足所选路线总路程为 S 到 F 的最小路程,要么满足所选路线总路程仅比最小路程多一个单位长度。

如上图所示,如果 S=1,F=5,则这里有两条最短路线 1→2→5,1→3→5,长度为 6;有一条比最短路程多一个单位长度的路线 1→3→4→5,长度为 7。现在给定比荷卢经济联盟的公交路线图以及两个城市 S 和 F,请你求出旅行社最多可以为旅客提供多少种不同的满足限制条件的线路。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。每组数据第一行包含两个整数 N 和 M,分别表示总城市数量和道路数量。接下来 M 行,每行包含三个整数 A,B,L,表示有一条线路从城市 A 通往城市 B,长度为 L。需注意,线路是单向的,存在从 A 到 B 的线路不代表一定存在从 B 到 A 的线路,另外从城市 A 到城市 B 可能存在多个不同的线路。接下来一行,包含两个整数 S 和 F,数据保证 S 和 F 不同,并且 S、F 之间至少存在一条线路。

输出格式

每组数据输出一个结果,每个结果占一行。数据保证结果不超过 10 ^ 9。

数据范围

2 ≤ N ≤ 1000,
1 ≤ M ≤ 10000,
1 ≤ L ≤ 1000,
1 ≤ A,B,S,F ≤ N

输入样例:

2
5 8
1 2 3
1 3 2
1 4 5
2 3 1
2 5 3
3 4 2
3 5 4
4 5 3
1 5
5 6
2 3 1
3 2 1
3 1 10
4 5 2
5 2 7
5 2 7
4 1

输出样例:

3
2
来源:https://www.acwing.com/problem/content/385/

2. 思路分析:

分析题目可以知道我们需要求解从S到T的最短路径和次短路径的条数,因为需要维护最短路径和次短路径的相关参数所以我们需要两维数组来表示(类似于拆点的过程),其中d(i,0)表示从S到i的最短路径长度,d(i,1)表示从S到i的次短路径长度,cnt(i,0)表示S到i的最短路径的条数,cnt(i,1)表示S到i的次短路径的条数,因为边权大于1所以我们可以dijkstra算法来求解,使用dijkstra算法求解的一个好处是在求解最短路径的时候能够保证节点与节点之间满足拓扑序(如果使用spfa算法,在求解最短路径与次短路径的时候不能够同时维护最短路径条数与次短路径的条数,我们需要先做一遍最短路径算法求解出最短路径与次短路径,然后创建最短路径拓扑图最后再求解最短路径条数与次短路径条数,求解的过程会很麻烦),这样我们就可以在求解最短路径与次短路径的过程中维护最短路径的条数与次短路径的条数,主要的思想是在求解从S到T的最短路径中来更新最短路径、次短路径的长度,最短路径的条数,次短路径的条数,主要是分为四种情况进行更新,分为当前弹出堆中的节点作为中间节点到达其余点的最短距离更近或者是相等,到达其余点的次短路径更近或者是相等,分情况更新即可。

3. 代码如下:

from typing import List
import heapq


class Solution:
    # 堆优化版的dijkstra算法, 只要距离有更新那么就将点的信息加入到堆中
    def dijkstra(self, s: int, t: int, n: int, g: List[List[int]]):
        INF = 10 ** 10
        dis = [[INF] * 2 for i in range(n + 1)]
        dis[s][0] = 0
        cnt = [[0] * 2 for i in range(n + 1)]
        cnt[s][0] = 1
        vis = [[0] * 2 for i in range(n + 1)]
        # 堆中需要保存三个信息, 这三个信息封装为一个列表, 第一个是起点到当前点的最短距离, 第二个是节点编号, 第三个是类型, 0表示最短路, 1表示次短路
        # python使用heapq模块需要使用借助于一个列表q
        q = list()
        # 加入起点
        heapq.heappush(q, [0, s, 0])
        while q:
            p = heapq.heappop(q)
            d, idx, type = p[0], p[1], p[2]
            if vis[idx][type] == 1: continue
            vis[idx][type] = 1
            # 遍历当前节点的邻接点
            for next in g[idx]:
                ver = next[0]
                # 主要分为四种情况来更新最短路和次短路
                if dis[ver][0] > next[1] + d:
                    dis[ver][1] = dis[ver][0]
                    cnt[ver][1] = cnt[ver][0]
                    heapq.heappush(q, [dis[ver][1], ver, 1])
                    cnt[ver][0] = cnt[idx][type]
                    dis[ver][0] = next[1] + d
                    heapq.heappush(q, [dis[ver][0], ver, 0])
                elif dis[ver][0] == next[1] + d:
                    cnt[ver][0] += cnt[idx][type]
                elif dis[ver][1] > next[1] + d:
                    cnt[ver][1] = cnt[idx][type]
                    dis[ver][1] = next[1] + d
                    heapq.heappush(q, [dis[ver][1], ver, 1])
                elif dis[ver][1] == next[1] + d:
                    cnt[ver][1] += cnt[idx][type]
        res = cnt[t][0]
        # 判断次短路是否是最短路的长度加1
        if dis[t][0] + 1 == dis[t][1]: res += cnt[t][1]
        return res

    def process(self):
        T = int(input())
        while T > 0:
            n, m = map(int, input().split())
            g = [list() for i in range(n + 1)]
            for i in range(m):
                # 有向边
                x, y, z = map(int, input().split())
                g[x].append((y, z))
            s, t = map(int, input().split())
            print(self.dijkstra(s, t, n, g))
            T -= 1


if __name__ == "__main__":
    Solution().process()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值