洛谷P4568 [JLOI2011]飞行路线(分层图最短路)

题目描述

A l i c e Alice Alice B o b Bob Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n n n个城市设有业务,设这些城市分别标记为 0 0 0 n − 1 n-1 n1,一共有 m m m种航线,每种航线连接两个城市,并且航线有一定的价格。

A l i c e Alice Alice B o b Bob Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k k k种航线上搭乘飞机。那么 A l i c e Alice Alice B o b Bob Bob这次出行最少花费多少?

输入输出格式

输入格式:
数据的第一行有三个整数, n , m , k n,m,k n,m,k,分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数, s , t s,t s,t,分别表示他们出行的起点城市编号和终点城市编号。
接下来有m行,每行三个整数, a , b , c a,b,c a,b,c,表示存在一种航线,能从城市 a a a到达城市 b b b,或从城市 b b b到达城市 a a a,价格为 c c 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

关于分层图最短路

(以下来自度娘)
分层图最短路是指在可以进行分层图的图上解决最短路问题。

一般模型是:

在图上,有k次机会可以直接通过一条边(也就是免费),问起点与终点之间的最短路径。

一般解决这种问题的时候,我们要将最短路的 d i s dis dis 数组开成两维。
d i s [ i ] [ j ] dis[i][j] dis[i][j] 表示我走到了 i i i号点,使用了 j j j次机会的最短路径是多少。

如何更新?

d i s dis dis数组可以由两个方式更新,对应着两种操作——在这个点我用不用一次机会。
我们设边 E ( u , v ) E(u , v) E(u,v)
如果用这次机会 d i s [ v ] [ j ] = d i s [ u ] [ j − 1 ] dis[v][j] = dis[u][j-1] dis[v][j]=dis[u][j1]
如果不用这次机会 d i s [ v ] [ j ] = d i s [ u ] [ j ] + v a l ( u , v ) dis[v][j] = dis[u][j] + val(u,v) dis[v][j]=dis[u][j]+val(u,v)
这些操作我们完全可以在SPFA或Dijkstra更新最短路时实现。

还要注意一个坑点 这个题的编号是从 0 0 0 开始的,如果不习惯的话输入时要把编号加上 1 1 1

code

#include<bits/stdc++.h>

using namespace std;

const int maxn = 10005, maxm = 100005;
struct node
{
	int f, t, v;
}e[maxm];
int n, m, tot, k, S, T, ans = 1e9;
int head[maxn], nxt[maxm], used[maxn] , dis[maxn][12];
#define ri register int
inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
	return x * f;
}
inline void buildnode(int a, int b, int c)
{
	tot++;
	e[tot].f = a;
	e[tot].t = b;
	e[tot].v = c;
	nxt[tot] = head[a];
	head[a] = tot;
}
typedef pair<int, int>p;
struct cmp
{
	bool operator()(p a, p b)
	{
		return a.first > b.first;
	}
};
priority_queue<p, vector<p>, cmp>q;
inline void dijkstra(int s)
{
	for (ri i = 1; i <= n; i++)
		for (ri j = 0; j <= 10; j++) dis[i][j] = 1e9;
	dis[s][0] = 0;
	q.push(p(dis[s][0], s));
	while (!q.empty())
	{
		p x = q.top();
		q.pop();
		int flag = 0;
		for (ri i = head[x.second]; i; i = nxt[i])
		{
			int u = e[i].t;
			for(ri j = 0 ; j <= k ; j ++)
			if (dis[u][j] > dis[x.second][j] + e[i].v)
			{
				dis[u][j] = dis[x.second][j] + e[i].v;
				q.push(p(dis[u][j], u));
				flag = 1;
			}
			for (ri j = 1; j <= k; j++)
			{
				if (dis[u][j] > dis[x.second][j - 1])
					dis[u][j] = dis[x.second][j - 1], flag = 1;
			}
			if (flag == 1) q.push(p(dis[u][0], u));
		}
	}
}
int main()
{
	n = read(); m = read(); k = read();
	S = read(); T = read();
	S += 1; T += 1;
	for (ri i = 1; i <= m; i++)
	{
		int a, b, c;
		a = read(); b = read(); c = read();
		a += 1, b += 1;
		buildnode(a, b, c);
		buildnode(b, a, c);
	}
	dijkstra(S);
	for (ri i = 0; i <= k; i++) ans = min(ans, dis[T][i]);
	printf("%d", ans);
	//system("pause");
}

End

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值