P3259 [JLOI2014] 路径规划 题解
P3259 [JLOI2014] 路径规划
如此分层图好题做的人却不多,不知为何。
红绿灯的期望通过时间
省流:\(\dfrac{a^2}{2(a+b)}\)。具体计算过程如下:
首先标签带一个 数学 的原因就是在于红绿灯期望通过时间的计算。由于我们可以在任意时刻进入这个红绿灯,由于一个红绿灯的周期是 \([0,a+b]\),其中 \(a\) 是红灯时长,\(b\) 是绿灯时长。考虑设我们在第 \(x\) 个时刻进入这个红绿灯,其中 \(x\in[0,a+b]\),那么有两种情况:
- \(a\le x\le a+b\),此时不需要等,期望通过时间为 \(0\);
- \(0\le x<a\),此时需要等的时间为 \(a-x\)。
于是容易得到对于任意的 \(x\in[0,a)\),我们的期望通过时间为 \(\dfrac{a-x}{a+b}\)。实际上如果我们会积分,这个东西就是
但是我们不会积分,于是考虑作出函数图象 \(y=\dfrac{a-x}{a+b}\),我们要求的就是这个函数图象和 \(x\) 轴、\(y\) 轴围成的一个三角形的面积。显然这是个一次函数,经计算可得到与 \(x\) 轴的交点 \(A(a,0)\),与 \(y\) 轴的交点为 \(B\bigg(0,\dfrac a{a+b}\bigg)\),所以
这个东西就是这么来的。
知道了每个红绿灯的期望通过时间,考虑直接把这个时间加到这个点出边的边权里,这个部分就解决了。
分层图最短路
考虑分层图的原因是:本题的最短路有两个限制:油量的限制 \(\mathit{limit}\),以及不能走超过 \(k\) 个红绿灯。并且 \(\mathit{limit}\) 和 \(k\) 都很小,这就妥妥的分层图最短路。
但是分层图最短路只能解决一个限制,不妨先解决红绿灯,因为 \(k\) 比 \(\mathit{limit}\) 要小,并且 “不能超过” 这个限制比油量的限制好实现一些,设计第 \(i\) 层图表示经过 \(i\) 个红绿灯的最短路,在红绿灯节点上连接相邻两层。
如果没有油量的限制,我们直接从第零层的起点跑最短路,最后在终点的各个层统计 \(\text{dis}\) 的最小值即可。但是做题没有如果,现在我们要加上油量的限制,虽然不能通过分层图解决,但是我们发现 \(\mathit{limit}\) 也很小,而除了加油站之外的点没什么用。所以考虑先建出原图的分层图,枚举每个加油站 \(x\) 作为起点跑最短路,然后看对于其他的加油站 \(y\) 的每一层的点,有哪些点是能用一箱油跑到的,然后每层之间连接 \(x\to y\) 的边,建出一个新图。
这个新图也是有 \(k\) 层,每层的点不超过 \(\mathit{limit}\) 个。在新图上第零层的起点跑最短路,最后在终点的各个层统计 \(\text{dis}\) 的最小值即可。
时间复杂度方面,由于原图有 \(nk\) 个节点,而瓶颈在于我们需要跑 \(\mathit{limit}\) 遍原图的最短路,应该是 \(O(\mathit{limit}\times mk\log nk)\),总之是能过且很快。
#include<bits/stdc++.h>
using namespace std;
constexpr int MAXN=1.1e6+5;
constexpr double INF=156842099845.0,eps=1e-10;
int n,m,k,limit,cost,S,T;
int id[11][MAXN/10];
unordered_map<string,int>mp;
vector<int>gas;
double out[MAXN];
struct{
int head[MAXN],tot;
struct{
int v,to;
double w;
}e[MAXN<<1];
void addedge(int u,int v,double w){
e[++tot]={v,head[u],w};
head[u]=tot;
}
void addf(int u,int v,double w){
if(fabs(out[v])>eps)
for(int i=0;i<k;i++)
addedge(id[i][u],id[i+1][v],w+out[v]);
else
for(int i=0;i<=k;i++)
addedge(id[i][u],id[i][v],w);
}
double dis[MAXN];
bool vis[MAXN];
void dijkstra(int s){
memset(dis,0x42,(n*(k+1)+1)<<3); // double也是可以memset的哦!
memset(vis,0,n*(k+1)+1);
priority_queue<pair<double,int>>q;
dis[s]=0;
q.emplace(0,s);
while(!q.empty()){
int u=q.top().second;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u],v;i;i=e[i].to)
if(dis[v=e[i].v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
q.emplace(-dis[v],v);
}
}
}
}G,GF; // G原图,GF新图
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
cin>>n>>m>>k>>limit>>cost;
for(int i=0;i<=k;i++)
for(int j=1;j<=n;j++)
id[i][j]=i*n+j;
for(int i=1;i<=n;i++){
string s;
int a,b;
cin>>s>>a>>b;
mp[s]=i;
if(s=="start"||s=="end"||s.find("gas")!=string::npos) gas.emplace_back(i);
if(s=="start") S=i;
else if(s=="end") T=i;
if(a) out[i]=1.0*a*a/2/(a+b);
}
for(int i=1;i<=m;i++){
string u,v,fk;
int w;
cin>>u>>v>>fk>>w;
G.addf(mp[u],mp[v],w);
G.addf(mp[v],mp[u],w);
}
for(auto x:gas){
G.dijkstra(id[0][x]);
for(auto y:gas){
if(x==y) continue;
int w=(x!=S&&x!=T?cost:0);
for(int i=0;i<=k;i++)
if(G.dis[id[i][y]]<=limit)
for(int j=0;i+j<=k;j++)
GF.addedge(id[j][x],id[i+j][y],G.dis[id[i][y]]+w);
}
}
GF.dijkstra(id[0][S]);
double ans=INF;
for(int i=0;i<=k;i++) ans=min(ans,GF.dis[id[i][T]]);
cout<<fixed<<setprecision(3)<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号