Description
Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价格。Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?
Input
数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。(0<=s,t
Output
只有一行,包含一个整数,为最少花费。
Sample Input
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
Sample Output
8
HINT
对于30%的数据,2<=n<=50,1<=m<=300,k=0;
对于50%的数据,2<=n<=600,1<=m<=6000,0<=k<=1;
对于100%的数据,2<=n<=10000,1<=m<=50000,0<=k<=10.
思路
分层图最短路的模板题,此题洛谷上卡SPFA
,所以用堆优化的dijkstra
来做.
题意就是,给出你一张无向图,你有k
次机会使得某一条边的花费为0
,求最小花费.
我们需要利用类似动态规划的思想来进行转移。
我们定义:
-
dis[i][j]
d
i
s
[
i
]
[
j
]
:从起点
st
到i
点,使用了j
次优惠机会所使用的最小花费. -
vis[i][j]
v
i
s
[
i
]
[
j
]
:从起点
st
到i
点,使用了j
次优惠机会这个状态有没有被标记
我们需要对普通的迪杰斯特拉转移的时候进行一些改变:
不使用免费机会的时候:
如果 到点u的花费dis[u][k]+从u到v的边权w<到v的边权dis[v][k] 正常转移更新:dis[v][k]=dis[u][k]+w 加入堆q.push(node(v,k,dis[v][k]))
使用免费机会的时候:
如果 到点u的时候使用了k次机会的花费dis[u][k]<到v点的时候使用了k+1次机会的边权dis[v][k+1] 这条边免费更新dis[v][k+1]=dis[u][k] 加入堆q.push(node(v,k+1,dis[v+1][k]))
剩下的就是迪杰斯特拉的堆优化模板了。
代码
#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int n, m, k;
int first[N], tot;
struct edge
{
int v, w, next;
} e[N * 2];
void add_edge(int u, int v, int w)
{
e[tot].v = v, e[tot].w = w;
e[tot].next = first[u];
first[u] = tot++;
}
struct node
{
int id, now, k;
node() {}
node(int _id, int _k, int _now)
{
id = _id, now = _now, k = _k;
}
bool friend operator<(node a, node b)
{
return a.now > b.now;
}
};
/*
dis[i][j]:表示从st到i点用了j次免费机会的最小花费
vis[i][j]:表示从st到i点用了j次免费机会有没有被标记过
*/
int dis[N][12], vis[N][12];
void dijkstra(int st)
{
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= k; j++)
{
dis[i][j] = inf;
vis[i][j] = 0;
}
}
dis[st][0] = 0;
priority_queue<node> q;
q.push(node(st, 0, 0));
while (!q.empty())
{
node u = q.top();
q.pop();
if (!vis[u.id][u.k])
{
vis[u.id][u.k] = 1;
for (int i = first[u.id]; ~i; i = e[i].next)
{
int v = e[i].v, w = e[i].w;
if (!vis[v][u.k] && dis[u.id][u.k] + w < dis[v][u.k])
{
dis[v][u.k] = dis[u.id][u.k] + w;
q.push(node(v, u.k, dis[v][u.k]));
}
if (u.k < k && !vis[v][u.k + 1] && dis[u.id][u.k] < dis[v][u.k + 1])
{
dis[v][u.k + 1] = dis[u.id][u.k];
q.push(node(v, u.k + 1, dis[v][u.k + 1]));
}
}
}
}
}
void init()
{
mem(first, -1);
tot = 0;
}
int main()
{
//freopen("in.txt", "r", stdin);
int u, v, w, st, ed;
scanf("%d%d%d%d%d", &n, &m, &k, &st, &ed);
st++, ed++;
init();
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w);
u++, v++;
add_edge(u, v, w);
add_edge(v, u, w);
}
dijkstra(st);
int ans = inf;
for (int i = 0; i <= k; i++)
ans = min(ans, dis[ed][i]);
printf("%d\n", ans);
return 0;
}