P3259 [JLOI2014] 路径规划 题解

P3259 [JLOI2014] 路径规划

如此分层图好题做的人却不多,不知为何。

红绿灯的期望通过时间

省流:a22(a+b)。具体计算过程如下:

首先标签带一个 数学 的原因就是在于红绿灯期望通过时间的计算。由于我们可以在任意时刻进入这个红绿灯,由于一个红绿灯的周期是 [0,a+b],其中 a 是红灯时长,b 是绿灯时长。考虑设我们在第 x 个时刻进入这个红绿灯,其中 x[0,a+b],那么有两种情况:

  • axa+b,此时不需要等,期望通过时间为 0
  • 0x<a,此时需要等的时间为 ax

于是容易得到对于任意的 x[0,a),我们的期望通过时间为 axa+b。实际上如果我们会积分,这个东西就是

0aaxa+bdx=a22(a+b)

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

SOAB=12×a×aa+b=a22(a+b)

这个东西就是这么来的。

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

分层图最短路

考虑分层图的原因是:本题的最短路有两个限制:油量的限制 limit,以及不能走超过 k 个红绿灯。并且 limitk 都很小,这就妥妥的分层图最短路。

但是分层图最短路只能解决一个限制,不妨先解决红绿灯,因为 klimit 要小,并且 “不能超过” 这个限制比油量的限制好实现一些,设计第 i 层图表示经过 i 个红绿灯的最短路,在红绿灯节点上连接相邻两层。

如果没有油量的限制,我们直接从第零层的起点跑最短路,最后在终点的各个层统计 dis 的最小值即可。但是做题没有如果,现在我们要加上油量的限制,虽然不能通过分层图解决,但是我们发现 limit 也很小,而除了加油站之外的点没什么用。所以考虑先建出原图的分层图,枚举每个加油站 x 作为起点跑最短路,然后看对于其他的加油站 y 的每一层的点,有哪些点是能用一箱油跑到的,然后每层之间连接 xy 的边,建出一个新图。

这个新图也是有 k 层,每层的点不超过 limit 个。在新图上第零层的起点跑最短路,最后在终点的各个层统计 dis 的最小值即可。

时间复杂度方面,由于原图有 nk 个节点,而瓶颈在于我们需要跑 limit 遍原图的最短路,应该是 O(limit×mklognk),总之是能过且很快。

#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 @   Laoshan_PLUS  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示