1. 程式人生 > 其它 >AcWing 383. 觀光

AcWing 383. 觀光

題目傳送門

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int M = 10010;

int n, m;      // n個節點,m條邊
int d[N][2];   //拆點,因為每個點都要計算最短路和次短路,所以一個點看到兩個點
int cnt[N][2]; //最短路和次短路的路線個數
bool st[N][2]; //一個st拆成兩個
int S, F;      //起點和終點
struct Node {
    int dist; //用於Dijkstra中排序的最短距離
    int id;   //節點號
    int type; // 0:最短路,1:次短路
};
//小頂堆,結構體對比,需要過載大於號。如果a.dist>b.dist,則表示
// a>b,因為是小頂堆,所以b在前,即dist越小越靠前
bool operator>(const Node &a, const Node &b) {
    return a.dist > b.dist;
}

priority_queue<Node, vector<Node>, greater<Node>> q;
//鄰接表
int idx, h[N], e[M], w[M], ne[M];
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
//迪傑斯特拉
int dijkstra() {
    memset(d, 0x3f, sizeof d);    //初始化距離
    memset(cnt, 0, sizeof cnt);   //記錄最短邊個數,次短邊個數
    memset(st, false, sizeof st); //沒有在佇列中
    d[S][0] = 0;                  //起點最短路長度為0,起點次長路徑不存在,為INF
    cnt[S][0] = 1;                //起點最短路個數為1,起點次長路徑不存在,為INF

    //距離,節點號,型別:相對於拆點,一分為二
    q.push({d[S][0], S, 0});
    while (q.size()) {
        auto t = q.top();
        q.pop();
        // u:節點號 type:型別(最短,次短)
        int u = t.id, type = t.type;
        //二維狀態描述是不是已經入過隊了
        if (st[u][type]) continue;

        //入隊
        st[u][type] = true;
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            int dist = d[u][type] + w[i]; //可以用來鬆馳的可能距離

            //這裡將“比最短路長度多1的距離”定義為次短距離
            //與正常含義的次短距離不同,需要注意區分,否則理解不了

            //小於最短距離
            if (dist < d[j][0]) {
                //如果最短距離可以變為次短距離
                if (dist + 1 == d[j][0]) {
                    d[j][1] = d[j][0];       //用最短距離更新次短距離
                    cnt[j][1] = cnt[j][0];   //最短距離數量更新次短距離數量
                    q.push({d[j][1], j, 1}); //次短距離入佇列
                }
                d[j][0] = dist;          //更新最短距離
                cnt[j][0] = cnt[u][0];   //更新最短距離的數量
                q.push({d[j][0], j, 0}); //最短距離入佇列
            }
            //等於最短距離
            else if (dist == d[j][0])
                cnt[j][0] += cnt[u][0]; //最短距離數累加
            //等於次短距離
            else if (dist == d[j][0] + 1) {
                if (!cnt[j][1]) {            //沒有記錄過次短距離
                    d[j][1] = dist;          //記錄上
                    q.push({d[j][1], j, 1}); //次短距離入佇列
                }
                //這裡一定要寫成type,因為type是0或者1都可能觸發該分支
                cnt[j][1] += cnt[u][type];
            }
        }
    }
    //返回終點最短距離和次短距離的和
    return cnt[F][0] + cnt[F][1];
}
int main() {
    int T;
    // T組測試資料
    cin >> T;
    while (T--) {
        //多組測試資料,需要初始化
        memset(h, -1, sizeof h);
        idx = 0;
        // n個節點,m條邊
        cin >> n >> m;
        int a, b, c;
        while (m--) {
            cin >> a >> b >> c;
            add(a, b, c);
        }

        cin >> S >> F;
        printf("%d\n", dijkstra());
    }
    return 0;
}