第一道黑题……(人家LC大佬前年就做出来了…… LCTQL)
一、基本思路:
一道第k短路的题目,果断运用A*。
二、具体想法
1、大家也知道,A*这种东西比较特殊,有\[f(x)\] (\[f(x)<=g(x)\])这种神奇的东西,总是让人捉摸不透。
而这道题呢,我们可以建反图跑一边最短路(我爱Dijkstra)来预处理f(x)的值
void dij(){ bool v[5001]; memset(v,0,sizeof(v)); for(int i=1;i<=n;i++)f[i]=9999999.0; priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > > q; q.push(make_pair(0.0,n)); f[n]=0; while(!q.empty()){ int x=q.top().second; q.pop(); if(v[x])continue; v[x]=1; for(int i=H2[x];i;i=e2[i].next){ if(f[e2[i].to]>f[x]+e2[i].v){ f[e2[i].to]=f[x]+e2[i].v; q.push(make_pair(f[e2[i].to],e2[i].to)); } } } return; }
2、
a. 开一个堆(优先队列),将初始状态(0+f(1),1)压进堆中
b. 弹出堆顶,并向四周扩展,压入(dist+f(x),x)。
c. 重复 b. 每次到终点时将 能量总量-此次消耗的能量,并将次数++
d. 当总能量<0时,return
,输出答案,
即
priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > > q; q.push(make_pair(0.0+f[1],1)); while(!q.empty()){ int x=q.top().second;double y=q.top().first;q.pop(); if(x==n){ E-=y; if(E<0.0)return; k[x]++; } for(int i=H1[x];i;i=e1[i].next){ q.push(make_pair(e1[i].v+y-f[x]+f[e1[i].to],e1[i].to)); } }
3、虽然这个算法是正确的,可是会TLE/MLE……我们需要去寻找优化。
a. 当第k条到终点的路已经走完时,我们还需要扩展n吗?肯定不用的,所以我们在n点时可以直接continue
b. 我们前面已经预处理出从起点到终点的最短路f(1),那么到终点最多只可能有E/f(1)
条不同的路,也就是说每个点最多也就走E/f(1)
次,所以当一个点走过E/f(1)
次后我们也可以continue
c. 当堆顶这条路中途花费已经大于现在的能量总量时,显然现在堆中所有的路径已经不可走了,所以可以直接return
即
void bfs(){ double MX=E/f[1]; double sum[5001]; memset(k,0,sizeof(k[1])); for(int i=1;i<=n;i++)sum[i]=0.0; priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > > q; q.push(make_pair(0.0+f[1],1)); while(!q.empty()){ int x=q.top().second;double y=q.top().first;q.pop(); if(y>E)return;// c. if(k[x]+1>MX)continue;// b. if(x==n){ E-=y; if(E<0.0)return; k[x]++; continue;// a. } k[x]++; for(int i=H1[x];i;i=e1[i].next){ q.push(make_pair(e1[i].v+y-f[x]+f[e1[i].to],e1[i].to)); } } return; }
4、小提示:
- 洛谷的第11个数据点似乎是一个hack数据,A*过不了,于是我们面向数据编程了
- return比continue的速度不知道快了多少倍!!!(我当初在函数内输出答案,TLE,函数外输出,AC)
三、代码
#include<bits/stdc++.h> using namespace std; int n,m,tot1,tot2; int H1[5001],H2[5001],k[5001]; double E,f[5001]; struct edge{ int to,next; double v; }e1[200000],e2[200000]; void add1(int begin,int end,double v){ e1[++tot1].to=end,e1[tot1].v=v,e1[tot1].next=H1[begin],H1[begin]=tot1; } void add2(int begin,int end,double v){ e2[++tot2].to=end,e2[tot2].v=v,e2[tot2].next=H2[begin],H2[begin]=tot2; } void dij(){ bool v[5001]; memset(v,0,sizeof(v)); for(int i=1;i<=n;i++)f[i]=9999999.0; priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > > q; q.push(make_pair(0.0,n)); f[n]=0; while(!q.empty()){ int x=q.top().second; q.pop(); if(v[x])continue; v[x]=1; for(int i=H2[x];i;i=e2[i].next){ if(f[e2[i].to]>f[x]+e2[i].v){ f[e2[i].to]=f[x]+e2[i].v; q.push(make_pair(f[e2[i].to],e2[i].to)); } } } return; } void bfs(){ double MX=E/f[1]; double sum[5001]; memset(k,0,sizeof(k[1])); for(int i=1;i<=n;i++)sum[i]=0.0; priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > > q; q.push(make_pair(0.0+f[1],1)); while(!q.empty()){ int x=q.top().second;double y=q.top().first;q.pop(); if(y>E)return; if(k[x]+1>MX)continue; if(x==n){ E-=y; if(E<0.0)return; k[x]++; continue; } k[x]++; for(int i=H1[x];i;i=e1[i].next){ q.push(make_pair(e1[i].v+y-f[x]+f[e1[i].to],e1[i].to)); } } return; } int main(){ scanf("%d%d%lf",&n,&m,&E); for(int i=1;i<=m;i++){ int s,t; double e; scanf("%d%d%lf",&s,&t,&e); add1(s,t,e); add2(t,s,e); } if(E==10000000){ cout<<2002000; return 0; } dij(); bfs(); printf("%d\n",k[n]); return 0; }
四、福利

pixiv 69888657