分层图最短路。。。听起来有点高大上,其实就只是相当于三维的最短路而已,和三维迷宫一样,我们用dis[i][k]表示起点S到第k层i的最短路,其中同层的图如果是无向图,继续保持,然后用一条由该层指向上一层的有向线段并且费用为0,作为该层到上一层的道路,实际上就是这样的:
由该层向上一层改点的相邻点建边,建完之后就可以直接开始跑最短路了,当然,有2种跑法,(一)可以将k层的点全部列出来,也就是总共k*n个点,然后建很多的边,最后跑最短路,结果访答案的时候:
for (int i=0; i<=k; i++) ans=min(ans,dis[t+i*n]);
(二)普通建边,在跑最短路的队列里面进行操作,这个就比较方便,下面给出代码:
void dij(int st,int ed,int k,int n) { priority_queue<node>q; q.push(node{st,0,0}); dis[st][0]=0; while (!q.empty()){ node now=q.top(); q.pop(); int id=now.id%n,fl=now.floor; if (vis[id][fl]) continue; vis[id][fl]=1; for (int i=head[id]; i!=-1; i=eg[i].next){//同层最短路 int v=eg[i].to; if (!vis[v][fl] && dis[v][fl]>dis[id][fl]+eg[i].w){ dis[v][fl]=dis[id][fl]+eg[i].w; q.push(node{v+n*fl,dis[v][fl],fl}); } } if (fl<k){ for (int i=head[id]; i!=-1; i=eg[i].next){//i层到i+1层最短路 int v=eg[i].to; if (!vis[v][fl+1] && dis[v][fl+1]>dis[id][fl]){ dis[v][fl+1]=dis[id][fl]; q.push(node{v+n*(fl+1),dis[v][fl+1],fl+1}); } } } } }
理解了这个核心代码之后那么也就不难写出分层图最短路的代码了,下面给出“洛谷P4568[JLOI2011]飞行路线”的题面和AC代码:
题目描述
Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?
输入格式
数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。
接下来有m行,每行三个整数,a,b,c,表示存在一种航线,能从城市a到达城市b,或从城市b到达城市a,价格为c。
输出格式
只有一行,包含一个整数,为最少花费。
输入输出样例
5 6 1 0 4 0 1 5 1 2 5 2 3 5 3 4 5 2 3 3 0 2 100
8
说明/提示
对于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,0≤s,t<n,0≤a,b<n,a≠b,0≤c≤1000
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=5e4+10; const int inf=5e8+10; struct Edge { int to,w,next; }eg[mac<<1]; struct node { int id,d,floor; bool operator <(const node &a)const{ return d>a.d; } }; int head[mac],num=0,vis[mac][15],dis[mac][15]; void add(int u,int v,int w) { eg[++num]=Edge{v,w,head[u]}; head[u]=num; } void dij(int st,int ed,int k,int n) { priority_queue<node>q; q.push(node{st,0,0}); dis[st][0]=0; while (!q.empty()){ node now=q.top(); q.pop(); int id=now.id%n,fl=now.floor; if (vis[id][fl]) continue; vis[id][fl]=1; for (int i=head[id]; i!=-1; i=eg[i].next){ int v=eg[i].to; if (!vis[v][fl] && dis[v][fl]>dis[id][fl]+eg[i].w){ dis[v][fl]=dis[id][fl]+eg[i].w; q.push(node{v+n*fl,dis[v][fl],fl}); } } if (fl<k){ for (int i=head[id]; i!=-1; i=eg[i].next){ int v=eg[i].to; if (!vis[v][fl+1] && dis[v][fl+1]>dis[id][fl]){ dis[v][fl+1]=dis[id][fl]; q.push(node{v+n*(fl+1),dis[v][fl+1],fl+1}); } } } } } int main() { int n,m,k,st,ed; scanf ("%d%d%d",&n,&m,&k); scanf ("%d%d",&st,&ed); memset(head,-1,sizeof head); for (int i=1; i<=m; i++){ int u,v,w; scanf ("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); } for (int i=0; i<n; i++) for (int j=0; j<=k; j++) dis[i][j]=inf; dij(st,ed,k,n); int ans=inf; for (int i=0; i<=k; i++) ans=min(ans,dis[ed][i]); printf ("%d ",ans); return 0; }