P4568 [JLOI2011] 飞行路线
written on 2022-05-26
第一次写分层图最短路的题,就以这道题作为一个模板题好了。
分层图最短路听起来是一个很新颖很高端的算法,但是其本质实际上很简单。
首先介绍其适用题目的特征:一般题目给出的信息会包括有几条免费路径,让你求出在有几条免费路径的前提下,到达终点的最短路径。
然后再来看看解决方法,这时分层图最短路就派上用场了。我们可以在原图的基础上,再建立 \(k\) 层的点,对于相邻不同层之间的点,连上边权为 \(0\) 的边,这样一来,我们将 选免费路径 视为跨层相邻的边,将 不选 视为同层的点。那么最终的答案就是所有层里面 \(End\) 的最小值。
要注意的点,就是数组的大小问题,因为 \(n\) 个点,扩展了 \(k\) 层,总共 \(k+1\) 层,因此 \(n\) 要开到原来的 \(k+1\) 倍大小,同理计算 \(m\),应为\(4\times (n+m)\times (k+1)\)。
细节处理详见代码
#include<bits/stdc++.h>
#define N 110005//n*(k+1)
#define M 2500005//4(n+m)k
using namespace std;
int n,m,k,s,t;
int tot,ver[M],edge[M],nxt[M],head[N];
void add_E(int x,int y,int z){ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;}
struct F
{
int x,v;
bool operator<(const F &p) const{return v>p.v;}
};
priority_queue<F> q;
int d[N];
bool mark[N];
void Dijkstra()
{
memset(d,0x7f,sizeof(d));
d[s]=0;
q.push((F){s,0});
while(q.size())
{
int x=q.top().x;
q.pop();
if(mark[x]) continue;
mark[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
q.push((F){y,d[y]});
}
}
}
}
int main()
{
scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
s++,t++;
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x++,y++;
for(int j=0;j<k;j++)
{
add_E(x+j*n,y+(j+1)*n,0),add_E(y+j*n,x+(j+1)*n,0);
add_E(x+j*n,y+j*n,z),add_E(y+j*n,x+j*n,z);
}
add_E(x+k*n,y+k*n,z),add_E(y+k*n,x+k*n,z);
}//41*50000=2050000
for(int i=1;i<=n;i++)
for(int j=0;j<k;j++) add_E(i+j*n,i+(j+1)*n,0);
//10000*10=100000
Dijkstra();
int ans=2e9;
for(int i=0;i<=k;i++) ans=min(ans,d[t+i*n]);
printf("%d\n",ans);
}

浙公网安备 33010602011771号