在郊区有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站 Ai 和 Bi。
特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。
现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费 Li。
电话公司正在举行优惠活动。
农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。
农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。
求至少用多少钱可以完成升级。
输入格式
第 1 行:三个整数 N,P,K。
第 2..P+1 行:第 i+1 行包含三个整数Ai,Bi,Li。
输出格式
包含一个整数表示最少花费。
若 1 号基站与 N 号基站之间不存在路径,则输出 −1。
数据范围
0≤K<N≤1000,
1≤P≤10000,
1≤Li≤1000000
输入样例:
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
输出样例:
4
题意: 有一个n点m边的无向图,还拥有k次免费路径机会,求从点1到点n的所有路径中最大边的最小值。
分析: 分层图上求最短路问题,是[分层图最短路]飞行路线 洛谷P4568 这道题的升级版,飞行路线只需要求出在k次免费路径下剩余路径长度,这个是非常容易获得的,但这道题要求的是从1到n的所有路径上第k+1大边的最小值,这有点类似最短路变形题,不过所有的一切都是建立在分层图基础上的。分层图上的层数表示使用了几条免费路径到达某点,再结合一下最短路变形的思想,这道题也就是求从1到第k层上n的所有路径上最大边的最小值,只需修改dijkstra松弛条件为:
if(dis[to][c] > max(dis[now][c], e[i].w))
{
dis[to][c] = max(dis[now][c], e[i].w);
a.push(make_pair(dis[to][c], to+c*n));
}
之后在分层图上跑一个dijkstra,然后统计dis[n][i]的最小值即可。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <queue>
#define int long long
#define pii pair<int, int>
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
//求从0到n-1的所有路径上第k+1大边的最小值
//也就是从0到第k层上n-1的所有路径上最大边的最小值
int n, m, k, head[1005], dis[1005][1005], cnt;//dis[i][j]表示到达i时使用了j次免费路径
bool vis[1005][1005];
struct edge
{
int to, next, w;
}e[20005];
void add(int u, int v, int w)
{
e[++cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
void dijkstra()
{
memset(dis, 0x3f, sizeof dis);
dis[0][0] = 0;
priority_queue<pii, vector<pii>, greater<pii> > a;
a.push(make_pair(dis[0][0], 0));
while(a.size())
{
int now = a.top().second;
int c = now/n;//表示在第几层上
now %= n;
a.pop();
if(vis[now][c]) continue;
vis[now][c] = true;
for(int i = head[now]; i; i = e[i].next)
{
int to = e[i].to;
if(dis[to][c] > max(dis[now][c], e[i].w))
{
dis[to][c] = max(dis[now][c], e[i].w);
a.push(make_pair(dis[to][c], to+c*n));
}
}
if(c < k)
for(int i = head[now]; i; i = e[i].next)
{
int to = e[i].to;
if(dis[to][c+1] > dis[now][c])
{
dis[to][c+1] = dis[now][c];
a.push(make_pair(dis[to][c+1], to+n*(c+1)));
}
}
}
}
signed main()
{
cin >> n >> m >> k;
for(int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%lld%lld%lld", &u, &v, &w);
u--, v--;
add(u, v, w), add(v, u, w);
}
dijkstra();
//枚举最小值是因为可能用不到k条免费路径就已经达到了0,之后会有一半情况为0,另一半不为0
int ans = inf;
for(int i = 0; i <= k; i++)
ans = min(dis[n-1][i], ans);
if(ans == inf)
puts("-1");
else
cout << ans << endl;
return 0;
}