You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 383. 观光

一、题目描述

您的个人假期 旅行社组织了一次比荷卢经济联盟的巴士之旅。

比荷卢经济联盟有很多公交线路。

每天公共汽车都会从一座城市开往另一座城市。

沿途汽车可能会在一些城市(零或更多)停靠。

旅行社计划旅途从 S 城市出发,到 F 城市结束。

由于不同旅客的景点偏好不同,所以为了迎合更多旅客,旅行社将为客户提供多种不同线路。

游客可以选择的行进路线有所限制: 要么满足所选路线总路程为 SF 的最小路程,要么满足所选路线总路程仅比最小路程多一个单位长度

如上图所示,如果 S=1F=5,则这里有两条最短路线 1→2→5,1→3→5,长度为 6;有一条比最短路程多一个单位长度的路线 1→3→4→5,长度为 7

现在给定比荷卢经济联盟的公交路线图以及两个城市 SF请你求出旅行社最多可以为旅客提供多少种不同的满足限制条件的线路

输入格式 第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 NM,分别表示总城市数量和道路数量。

接下来 M 行,每行包含三个整数 A,B,L,表示有一条线路从城市 A 通往城市 B,长度为 L

需注意,线路是 单向的,存在从 AB 的线路不代表一定存在从 BA 的线路,另外从城市 A 到城市 B 可能存在多个不同的线路。

接下来一行,包含两个整数 SF,数据保证 SF 不同,并且 S、F 之间至少存在一条线路。

输出格式 每组数据输出一个结果,每个结果占一行。

数据保证结果不超过 10^9

数据范围 2≤N≤1000,1≤M≤10000,1≤L≤10001≤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

二、解题思路

本题是在 AcWing 1134 最短路计数 的扩展版本

要求求出起点S到终点F最短次短 的路径的条数

状态表示

我们发现在上一题的基础上,只用一维的distcnt数组并不能表示 最短次短 两个状态,所以多开一维

设状态dist[i][0,1]表示初始城市S到城市i最短距离次短距离

cnt[i][0,1]表示城市S到城市i的最短路径和次短路经的 条数

初始 dist[S][0]=0cnt[S][0]=1

状态转移

枚举城市u可通往的城市v时,有四种情况:

1dist[v][0]>dist[u][k]+w[i],k∈[01]

更新最短路与次短路距离

  • 当前 最短路 变为 次短路,然后更新 最短路
  • 新变更最短路次短路 加入优先队列

更新最短路和次短路条数

  • 到达v的最短路个数和到达u是一样的 : cnt[v][0]=cnt[u][k]
2dist[v][0]=dist[u][k]+w[i],k∈[01]

找到一条新的 最短路,更新最短路条数 到达v的最短路个数应该加上到达u的最短路个数,从u经过的最短路,在v上经过的时候也是最短路:cnt[v][0]+=cnt[u][k]

3dist[v][1]>dist[u][k]+w[i]

找到一条更短的次短路,覆盖掉当前次短路,加入优先队列 到达v的最短路个数和到达u是一样的 : cnt[v][1]=cnt[u][k]

4dist[v][1]=dist[u][k]+w[i]

找到一条新的次短路,更新次短路条数 到达v的最短路个数应该加上到达u的最短路个数,从u经过的最短路,在v上经过的时候也是最短路:cnt[v][1]+=cnt[u][k]

特殊处理

最后到F城市的次短路径如果比最短路径恰好多1,满足题目要求,则把这样的路径条数加到答案里

Code

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
const int M = 10010;
int n, m;

int dist[N][2];
int cnt[N][2];
bool st[N][2];

// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

// 本题需要一个三个属性的对象最短距离d,最短、次短k,id:节点号
struct Node {
    int d, k, id;
    // 小顶堆需要重载大于号,大顶堆需要重载小于号
    bool const operator>(Node b) const {
        return d > b.d;
    }
};

void dijkstra(int S) {
    memset(dist, 0x3f, sizeof dist);
    memset(st, false, sizeof st);
    memset(cnt, 0, sizeof cnt);
    priority_queue<Node, vector<Node>, greater<>> pq; // 小顶堆
    dist[S][0] = 0;
    cnt[S][0] = 1;
    pq.push({0, 0, S});

    while (pq.size()) {
        auto t = pq.top();
        pq.pop();
        int u = t.id;
        int k = t.k;

        if (st[u][k]) continue;
        st[u][k] = true;

        for (int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            int d = dist[u][k] + w[i];

            if (dist[v][0] > d) {            // 比最短路还要短
                dist[v][1] = dist[v][0];     // 最短降为次短
                cnt[v][1] = cnt[v][0];       // 次短路数量被更新
                pq.push({dist[v][1], 1, v}); // 次短被更新,次短入队列

                dist[v][0] = d;              // 替换最短路
                cnt[v][0] = cnt[u][k];       // 替换最短路数量
                pq.push({dist[v][0], 0, v}); // 最短路入队列
            } else if (dist[v][0] == d)      // 增加最短路的数量
                cnt[v][0] += cnt[u][k];
            else if (dist[v][1] > d) { // 替换次短路
                dist[v][1] = d;
                cnt[v][1] = cnt[u][k];
                pq.push({dist[v][1], 1, v}); // 次短路入队列
            } else if (dist[v][1] == d)
                cnt[v][1] += cnt[u][k];
        }
    }
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        memset(h, -1, sizeof h);
        idx = 0;
        while (m--) {
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            add(a, b, c);
        }
        int S, F;
        scanf("%d %d", &S, &F);
        dijkstra(S);
        int ans = cnt[F][0]; // 最短路
        // 在正常处理完最短路和次短路后,在最后的逻辑中,增加本题的中特殊要求部分
        if (dist[F][0] == dist[F][1] - 1) ans += cnt[F][1];
        printf("%d\n", ans);
    }
    return 0;
}