洛谷P1266
Description
给定一张有向图,对于图中的每条边,给出它的长度以及经过它时的速度,当某条边给定的速度为 \(0\) 时,它的速度可以是任意一条连向这条边的起点的边的速度。
求从 \(0\) 号点出发,到达 \(D\) 号所用时间最少的一条路径。
Solution
根据题目信息,大概可以猜想到这是求一个最短路(或者在这个题目中叫做最快路)。
但是题目中只给出了路程与速度,并未给出时间,就要考虑该怎么转化。
显然,根据公式 \(\large{t=\frac{s}{v}}\),我们便可以将路程与速度转化成时间,然后将它作为边权跑一个最短路。
但是这里还有第二个问题,那就是某些边的速度不确定,也就是它的时间(边权)不确定,所以我们要通过某些方法确定它的速度。
如果直接贪心地钦定某条无速度的边是当前能连到它的最快速度,可能会出现这条路并不是最短路的情况。
所以这个速度并不能提前确定,而是在我们遍历的时候择优确定。
我们知道,在最短路中,\(dis_i\) 一般表示从起点到 \(i\) 号点的最短距离。
为了比较各个速度下的最短距离,我们可以在原基础上增加一维,也就是设 \(dis_{i,j}\) 表示从起点到第 \(i\) 号点,且在第 \(i\) 号点时速度为 \(j\) 的最短距离。
这样进行最短路更新时可以更新各个速度下的最短路,也就可以找出最快的路径了。
关于代码的实现
-
我们可以开一个结构体,存储每个点以及这个点开始的速度是多少,用于求最短路时的更新。
-
输出方案时,可以按最短路统计路径的方法,记录路径上每个点的前驱节点,然后递归输出即可。
-
注意点是从 \(0\) 到 \(n-1\) 编号的。
Code
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 2100
#define maxm 100100
#define INF 0x3f3f3f3f
//#define int long long
using namespace std;
int n,m,end,tot;
double Dis[maxn][maxn];
int vis[maxn][maxn],head[maxn];
struct node{int pos,vold;}pre[maxn][maxn];
struct edge{int to,v,len,nxt;}e[maxm];
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void add(int fr,int to,int v,int len){
e[++tot].to=to;e[tot].len=len;
e[tot].v=v;e[tot].nxt=head[fr];
head[fr]=tot;
}
void spfa(){
memset(pre,-1,sizeof pre);
memset(Dis,127,sizeof Dis);
Dis[1][70]=0;vis[1][70]=1;
queue<node> d;d.push((node){1,70});
while(!d.empty()){
int u=d.front().pos;
int v=d.front().vold;
d.pop();vis[u][v]=0;
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to,vold=e[i].v;
if(vold==0) vold=v;
if(Dis[to][vold]>Dis[u][v]+(double)(e[i].len/(vold/1.0))){
Dis[to][vold]=Dis[u][v]+(double)(e[i].len/(vold/1.0));
pre[to][vold].pos=u;pre[to][vold].vold=v;
if(!vis[to][vold]){
vis[to][vold]=1;
d.push((node){to,vold});
}
}
}
}
}
void print(int pos,int vold){
if(pre[pos][vold].pos!=-1)
print(pre[pos][vold].pos,pre[pos][vold].vold);
printf("%d ",pos-1);
}
int main(){
n=read();m=read();end=read()+1;
for(int i=1,fr,to,vold,len;i<=m;i++){
fr=read()+1;to=read()+1;
vold=read();len=read();
add(fr,to,vold,len);
}
spfa();double minx=INF*1.0,id;
for(int i=1;i<=500;i++)
if(minx>Dis[end][i])
minx=Dis[end][i],id=i;
print(end,id);
return 0;
}