A*(第k短路)

如果给定一个“目标状态”,需要求出从初态到目标状态的最小代价,那么优先队列BFS的这个“优先策略”显然是不完善的。一个状态的当前代价最小,只能说明从起始状态到该状态的代价很小,而在未来的搜索中,从该状态到目标状态可能会花费很大的代价:另外一些状态虽然当前代价略大,但是未来到目标状态的代价可能会很小,于是从起始状态到目标状态的总代价反而更优。优先队列BFS会优先选择前者的分支.

为了提高搜索效率,我们很自然地想到,可以对未来可能产生的代价进行预估。详细地讲,我们设计一个“估价函数”,以任意“状态”为输入,计算出从该状态到目标状态所需代价的估计值。在搜索中,我们仍然维护一一个堆,不断从堆中取出“当前代价+未来估价”最小的状态进行扩展。

为了保证第-次从堆中取出目标状态时得到的就是最优解,我们设计的估价函数需要满足一个基本准则: 设当前状态state到目标状态所需代价的估计值为f(state)。 设在未来的搜索中,实际求出的从当前状态state 到目标状态的最小代价为g(state)。 对于任意的state,应该有f(state) ≤g(state)。

也就是说,估价函数的估值不能大于未来实际代价,估价比实际代价更优。

很快就搜完了

于是乎,算出恰当的f很关键,f要尽量大但小于最优解

第K短路 

第几次出队就是第几短,于是终点出了k次就是第k短路了

按照dijsktra的思想,我们每次取出dis[x]+f[x]最小的

然后更新所有能到达的点

发现f[x]可以取到终点的dis,这样尽量大且一定比现在的解小

于是先倒着spfa一遍(搞出f)

然后A*,直到终点第k次走到

#include<bits/stdc++.h>
#define N 1005
#define M 100005
using namespace std;
int first[N],next[M*2],to[M*2],w[M*2],tot;
int first2[N],next2[M*2],to2[M*2],w2[M*2],tot2;
int dis[N],vis[N],n,m,s,t,k;
struct Node{
	int pos,f,dis;
	bool operator < (Node a) const{
		return a.f+a.dis<f+dis;
	}
};
priority_queue<Node> q; 
int read(){
	int cnt=0;char ch=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt;
}
void add(int x,int y,int z){
	next[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
	next2[++tot2]=first2[y],first2[y]=tot2,to2[tot2]=x,w2[tot2]=z;
}
void spfa(){
	queue<int> q1;
	q1.push(t);
	memset(dis,0x3fffffff,sizeof(dis));
	dis[t]=0;
	while(!q1.empty()){
		int x=q1.front();
		q1.pop(),vis[x]=0;
		for(int i=first2[x];i;i=next2[i]){
			int T=to2[i];
			if(dis[T]>dis[x]+w2[i]){
				dis[T]=dis[x]+w2[i];
				if(!vis[T]) q1.push(T),vis[T]=1;
			}
		}
	}
}
int astar(){
	if(dis[s]==0x3fffffff) return -1;
	int times[N];
	memset(times,0,sizeof(times));
	Node tmp,h;
	h.pos=s,h.f=0,h.dis=0;
	q.push(h);
	while(!q.empty()){
		Node x=q.top(); q.pop();
		times[x.pos]++;
		if(times[x.pos]==k&&x.pos==t) return x.dis;
		if(times[x.pos]>k) continue;
		for(int i=first[x.pos];i;i=next[i]){
			tmp.dis=x.dis+w[i];
			tmp.f=dis[to[i]];
			tmp.pos=to[i];
			q.push(tmp);
		}
	}
	return -1;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		add(x,y,z);
	}
	s=read(),t=read(),k=read();
	if(s==t) k++;
	spfa();
	cout<<astar();
	return 0;
} 

 

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值