分层图
BZOJ 飞行路线:传送门
分层图的思想可以解决这样的问题:有n次免费的机会下的最短路问题,例如飞行路线。
题意就是一个无向图,两点之间有费用。而你有k次免费搭乘飞机的机会。这种情况下问从a点到b点的最少消费。
这种问题是不能直接用最短路解决的。可以用DP的思想,dis[n][k]数组为到n这个点用了k次优惠的最少消费。这里就不展开。
我们现在介绍一种新思路。
我们可以构造k+1张该无向图,那么第i张图和第i+1张图如何连接呢,就是用已知的边把边权变为0连在两张图的端点上。
比如已知一条边(u,v,w),我们建图的时候就可以添加这样一条边(i*n+u,(i+1)*n+v,0)。
然后我们就有了一张有n*(k+1)个点的无向图,再直接用dijkstra就可以了。
来看代码
#include<stdio.h> #include<queue> #include<iostream> #define N 200010 #define WQD 2000000001 using namespace std; int num=0,m,n,k,s,t,head[N],dis[N]; struct EDGE { int w,to,next; }e[20*N]; void add(int f,int t,int w) { e[++num].next=head[f]; e[num].w=w; e[num].to=t; head[f]=num; } struct VD { int v,dis; bool operator < (const VD &a)const { return a.dis<dis; } }; void dijkstra(int st) { priority_queue <VD> q; for(int i=0;i<(k+1)*n;i++) dis[i]=WQD; dis[st]=0; VD temp; temp.v=st;temp.dis=0; q.push(temp); while(q.size()) { temp=q.top(); q.pop(); int p=temp.v; if(dis[p]<temp.dis) continue; for(int i=head[p];i;i=e[i].next) { if(dis[e[i].to]>dis[p]+e[i].w) { dis[e[i].to]=dis[p]+e[i].w; temp.v=e[i].to;temp.dis=dis[e[i].to]; q.push(temp); } } } } int main() { scanf("%d%d%d",&n,&m,&k); scanf("%d%d",&s,&t); for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); for(int j=0;j<=k;j++) //建k+1张图; { add(j*n+a,j*n+b,c); add(j*n+b,j*n+a,c); } for(int j=0;j<k;j++) //建边权为0的边连接两张相邻的图; { add(j*n+a,(j+1)*n+b,0); add(j*n+b,(j+1)*n+a,0); } } dijkstra(s); printf("%d",dis[k*n+t]); return 0; }
这样的操作让这种题目就变成了直接的最短路问题。
不过需要注意的是空间范围,因为我们要建立一个有n*(k+1),个点的无向图,会很大。所以一定要注意范围,不然会RE;