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}\)。实际上如果我们会积分,这个东西就是

\[\int_0^a\frac{a-x}{a+b}\,\mathrm dx=\frac{a^2}{2(a+b)} \]

但是我们不会积分,于是考虑作出函数图象 \(y=\dfrac{a-x}{a+b}\),我们要求的就是这个函数图象和 \(x\) 轴、\(y\) 轴围成的一个三角形的面积。显然这是个一次函数,经计算可得到与 \(x\) 轴的交点 \(A(a,0)\),与 \(y\) 轴的交点为 \(B\bigg(0,\dfrac a{a+b}\bigg)\),所以

\[S_{\triangle\text{OAB}}=\frac12\times a\times\frac a{a+b}=\frac{a^2}{2(a+b)} \]

这个东西就是这么来的。

知道了每个红绿灯的期望通过时间,考虑直接把这个时间加到这个点出边的边权里,这个部分就解决了。

分层图最短路

考虑分层图的原因是:本题的最短路有两个限制:油量的限制 \(\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;
}
posted @ 2025-03-04 21:39  Laoshan_PLUS  阅读(73)  评论(0)    收藏  举报