洛谷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;
}