今天想到了以前早就遗留下来的一个算法–A*算法,基本上,我对于A*只有过简单的了解,却从来没有具体的写过 A*,于是就有了今天的A*学习。
A*算法相比与其他的搜索来说,它多了一个估价函数而已。而估价函数在不同的题目中形式又不同,这正是A*难的地方啊~
对于此题:给出n个点,m条边,可能又重边,给定起点,终点,求k短路。
k短路是A*的一个最简单的应用。
我们可以设计这样的估价函数:
f(u)=g(u)+h(u).f来代表从s到t的总代价,g为s到u的总代价,h是剩下路径的总代价。于是我们可以从t跑一遍spfa,来求解出t到每个点的距离,然后将其加入优先队列中,我们可以知道,k短路即优先队列中第k个到达t的路径。
注意:估价函数的估计代价一定比实际代价少,这样才能是A*满足条件。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define Set(aa,bb) memset(aa,bb,sizeof(aa))
using namespace std;
const int maxn=100010,maxm=1010,inf=0x3f3f3f3f;
int be[maxn],ne[maxn],to[maxn],w[maxn],e;
int revbe[maxn],revne[maxn],revto[maxn],revw[maxn],reve;
int dis[maxm];
bool vis[maxn];
int n,m,s,t,k;
struct Astar{
int f,g,p;//f表示路程和,g表好已经走过的路程,h表示对应的点
bool operator <(const Astar a)const{
return f==a.f?g>a.g:f>a.f;
}
};
void add(int x,int y,int z){
ne[++e]=be[x],be[x]=e,to[e]=y,w[e]=z;
revne[e]=revbe[y],revbe[y]=e,revto[e]=x,revw[e]=z;
}
void spfa(int node){
For(i,1,n) dis[i]=inf,vis[i]=0;
dis[node]=0;
int q[maxn],f=0,l=0;
q[++l]=node;vis[node]=1;
while(f<l){
int ls=q[++f];vis[ls]=0;
for(int i=revbe[ls];i;i=revne[i]){
int u=revto[i];
if(dis[u]>dis[ls]+revw[i]){
dis[u]=dis[ls]+revw[i];
if(!vis[u]){
vis[u]=1;
q[++l]=u;
}
}
}
}
}
int A_star(int l,int r){
if(dis[l]==inf) return -1;
if(l==r) ++k;
priority_queue<Astar>q;
Astar now;
now.g=0,now.p=l,now.f=dis[l];
q.push(now);
int sum=0;
while(!q.empty()){
Astar ls=q.top();q.pop();
if(ls.p==r){
++sum;
if(sum==k) return ls.g;
}
for(int i=be[ls.p];i;i=ne[i]){
now.p=to[i];
now.g=ls.g+w[i];
now.f=now.g+dis[to[i]];
q.push(now);
}
}
return -1;
}
void work(){
scanf("%d%d",&n,&m);
For(i,1,m){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
scanf("%d%d%d",&s,&t,&k);
spfa(t);
printf("%d\n",A_star(s,t));
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
work();
return 0;
}