今天想到了以前早就遺留下來的一個算法–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;
}