洛谷P4568《飛行路線》
阿新 • • 發佈:2020-10-14
原更新日期: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;
}