【k短路&A*算法】BZOJ1975: [Sdoi2010]魔法猪学院

时间:2023-03-09 15:09:01
【k短路&A*算法】BZOJ1975: [Sdoi2010]魔法猪学院

Description

找出1~k短路的长度。

Solution

k短路的求解要用到A*算法

A*算法的启发式函数f(n)=g(n)+h(n)

g(n)是状态空间中搜索到n所花的实际代价

h(n)是n到结束状态最佳路径的估计代价

关于h(n)的选取,当h(n)<实际代价时,搜索慢但可出解;h(n)=实际代价时,正确率与效率最高;h(n)>实际代价,快但只能得到近似解。

但在k短路问题中,h(n)是可以选到准确值的,就是n到结束节点的最短路,预处理时从结束节点做一次单源最短路即可。

按广搜的方式扩展节点,每次优先扩展f(n)最小的节点。

第i次扩展到目标节点,代表找到了第i短路。

正确性什么的很好理解。

k短路关于A*部分代码很简洁,用优先队列维护。

这道题就是裸题,但这道题很丧病地让proirity_queueMLE,于是要手写堆。

让我这个几乎没手写过堆的STL狗QwQ

Code

 #include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N=5e3+,M=2e5+; double d[N];
int head[N],e[M],nxt[M],cnt;
double w[M];
int adde(int u,int v,double g){
e[++cnt]=v;w[cnt]=g;nxt[cnt]=head[u];head[u]=cnt;
}
int _head[N],_e[M],_nxt[M],_cnt;
double _w[M];
int _adde(int u,int v,double g){
_e[++_cnt]=v;_w[_cnt]=g;_nxt[_cnt]=_head[u];_head[u]=_cnt;
}
struct node{
double f,g;
int o;
bool operator<(const node&a)
const{return f<a.f;}
};
int n,m;
double c; queue<int>q;
int inque[N];
int spfa(){
memset(d,,sizeof(d));
d[n]=;
inque[n]=;
q.push(n); while(!q.empty()){
int u=q.front();q.pop();
for(int i=_head[u];i;i=_nxt[i]){
int v=_e[i];
if(d[v]>d[u]+_w[i]){
d[v]=d[u]+_w[i];
if(!inque[v]){
q.push(v);
inque[v]=;
}
}
}
inque[u]=;
}
} int ans,size;
node Q[];
int push(node x){
int now,next;
Q[++size]=x;
now=size;
while(now>){
next=now>>;
if(Q[next]<Q[now]) break;
swap(Q[now],Q[next]);
now=next;
}
}
node pop(){
int now,next;
node ret;
ret=Q[];
Q[]=Q[size--];
now=;
while((now<<)<=size){
next=now<<;
if(next<size&&Q[next+]<Q[next]) next++;
if(Q[now]<Q[next]) break;
swap(Q[now],Q[next]);
now=next;
}
return ret;
}
void Astar(){
push((node){d[],,});
while(size){
node x=pop();
for(int i=head[x.o];i;i=nxt[i]){
int v=e[i];
push((node){x.g+w[i]+d[v],x.g+w[i],v});
}
if(x.o==n){
c-=x.f;
if(c<) return;
ans++;
}
}
} int main(){
scanf("%d%d%lf",&n,&m,&c);
int u,v; double g;
for(int i=;i<=m;i++){
scanf("%d%d%lf",&u,&v,&g);
adde(u,v,g);
_adde(v,u,g);
} spfa();
Astar(); printf("%d\n",ans);
return ;
}