1. 程式人生 > 實用技巧 >洛谷P4568《飛行路線》

洛谷P4568《飛行路線》

原更新日期:2018-11-25 09:23:13

入門級別的分層圖最短路

題目地址
雙倍經驗

前言

先介紹一下分層圖最短路。

分層圖最短路是指在可以進行分層圖的圖上解決最短路問題。
一般模型是:
在圖上,有k次機會可以直接通過一條邊,問起點與終點之間的最短路徑。

題目描述

Alice和Bob現在要乘飛機旅行,他們選擇了一家相對便宜的航空公司。該航空公司一共在nn個城市設有業務,設這些城市分別標記為00到n-1n−1,一共有mm種航線,每種航線連線兩個城市,並且航線有一定的價格。

Alice和Bob現在要從一個城市沿著航線到達另一個城市,途中可以進行轉機。航空公司對他們這次旅行也推出優惠,他們可以免費在最多kk種航線上搭乘飛機。那麼Alice和Bob這次出行最少花費多少?

輸入輸出格式

輸入格式

資料的第一行有三個整數,n,m,k,分別表示城市數,航線數和免費乘坐次數。

第二行有兩個整數,s,t,分別表示他們出行的起點城市編號和終點城市編號。

接下來有m行,每行三個整數,a,b,c,表示存在一種航線,能從城市a到達城市b,或從城市b到達城市a,價格為c。

輸出格式

只有一行,包含一個整數,為最少花費。

輸入輸出樣例

輸入樣例#1:

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

輸出樣例#1:

8

解題思路

這就是分層圖最短路的模板
但為什麼是省選/NOI-

我們用DP的思想來看
dis[i][j]表示起點到i點在j

層的最短路

如何分層?
理解性記憶。
例如本題最多有十層,第k層表示免費了k次的最短路

如何跑最短路?
洛谷卡SPFA,BZOJ不卡SPFA,但是都要注意把空間開大10倍,不然是過不去的(5次TLE的慘痛經驗)
在跑 Dijkstra 的時候,我們用了一個pair來存當前到達的點和已走過的路徑;這次我們需要多維護一個東西:當前的層數。

struct Node {
	int id; // 當前到達的點
	int weight; // 已走過的路徑
	int now; // 當前的層數
	
	Node() {
		id = weight = now = 0;
	}
	
	// 過載運算子,用於優先佇列
	bool operator < (const Node &that) const {
		return weight > that.weight;
	}
};

在更新dis的時候,我們需要對這一層的點和下一層的點分別進行更新

if (!vis[to][Floor] && dis[to][Floor] > dis[now][Floor] + edge[e].weight) {
    dis[to][Floor] = dis[now][Floor] + edge[e].weight;
    q.push(NewNode(to, dis[to][Floor], Floor));
}

if (!vis[to][Floor] && Floor + 1 <= K && dis[to][Floor + 1] > dis[now][Floor]) {
    dis[to][Floor + 1] = dis[now][Floor];
    q.push(NewNode(to, dis[to][Floor + 1], Floor + 1));
}

程式碼實現

/* -- Basic Headers -- */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>

/* -- Defined Functions -- */
#define For(a,x,y) for (int a = x; a <= y; ++a)
#define Forw(a,x,y) for (int a = x; a < y; ++a)
#define Bak(a,y,x) for (int a = y; a >= x; --a)

namespace FastIO {
    inline int getint() {
        int s = 0, x = 1;
        char ch = getchar();
        while (!isdigit(ch)) {
            if (ch == '-') x = -1;
            ch = getchar();
        }
        while (isdigit(ch)) {
            s = s * 10 + ch - '0';
            ch = getchar();
        }
        return s * x;
    }
    inline void __basic_putint(int x) {
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        if (x >= 10) __basic_putint(x / 10);
        putchar(x % 10 + '0');
    }
    
    inline void putint(int x, char external) {
        __basic_putint(x);
        putchar(external);
    }
}


namespace Solution {
    const int MAXN = 100000 + 10;
    const int MAXM = 500000 + 10;
    const int MAXK = 10 + 5;
    
    struct Node {
        int id, weight, now;
        Node() {
            id = weight = now = 0;
        }
        bool operator < (const Node &that) const {
            return weight > that.weight;
        }
    } head[MAXN];
    
    struct Edge {
        int now, next, weight;
    } edge[MAXM];
    
    int n, m, k, s, t, K, cnt, dis[MAXN][MAXK];
    bool vis[MAXN][MAXK];
    
    inline void addEdge(int prev, int next, int weight) {
        edge[++cnt].now = next;
        edge[cnt].weight = weight;
        edge[cnt].next = head[prev].id;
        head[prev].id = cnt;
    }
    
    Node NewNode(int id, int weight, int now) {
        Node tmp;
        tmp.id = id;
        tmp.weight = weight;
        tmp.now = now;
        return tmp;
    }
    
    void SPFA() {
        memset(dis, 0x7f, sizeof(dis));
        std::priority_queue<Node> q;
        For (i, 0, K) dis[s][i] = 0;
        q.push(NewNode(s, 0, 0));
        while (!q.empty()) {
            Node NowNode = q.top();
            q.pop();
            int Floor = NowNode.now;
            int now = NowNode.id;
            if (vis[now][Floor]) continue;
            vis[now][Floor] = true;
            for (int e = head[now].id; e; e = edge[e].next) {
                int to = edge[e].now;
                if (!vis[to][Floor] && dis[to][Floor] > dis[now][Floor] + edge[e].weight) {
                    dis[to][Floor] = dis[now][Floor] + edge[e].weight;
                    q.push(NewNode(to, dis[to][Floor], Floor));
                }
                if (!vis[to][Floor] && Floor + 1 <= K && dis[to][Floor + 1] > dis[now][Floor]) {
                    dis[to][Floor + 1] = dis[now][Floor];
                    q.push(NewNode(to, dis[to][Floor + 1], Floor + 1));
                }
            }
        }
    }
}

signed main() {
    using namespace Solution;
    using FastIO::getint;
    n = getint();
    m = getint();
    k = getint();
    s = getint();
    t = getint();
    K = k;
    For (i, 1, m) {
        int prev = getint();
        int next = getint();
        int weight = getint();
        addEdge(prev, next, weight);
        addEdge(next, prev, weight);
    }
    SPFA();
    int ans = 2147482333;
    for (int i = 0; i <= k; ++i) {
        ans = std::min(ans, dis[t][i]);
    }
    FastIO::putint(ans, '\n');
    return 0;
}