题目链接:点击查看
题目大意:给出一个有向图,求第k短路
题目分析:偷学了一波A*,本来以为是多难的算法,其实就是bfs+优先队列的升级版,之前看的那些博客写的都太深奥了,以至于看了一半啥都没看懂然后就被吓跑了,这里放一波zx学长PPT上的描述,我感觉简洁精炼,几句话就把这个算法的核心描述清楚了:
- A * 算法的实现,A * =优先队列BFS+估价函数。
- 回顾优先队列bfs:优先队列BFS算法维护了一个优先队列,不断从堆中取出当前代价最小的状态进行扩展。每个状态第一次从堆中取出时,就得到了从初态到该状态的最小代价。
- 局限:如果给定一个目标状态,需要求出从初态到目标状态的最小代价,那么优先队列BFS这个优先策略是不完善的。一个状态当前代价小,只能说明从起始状态到该状态代价小,而在未来的搜索中从该状态到目标状态的花费可能会很大。导致有一部分很晚才能得到扩展。
- 为了提高搜索效率,我们很自然的想到,可以对未来可能产生的代价进行预估。
- 详细的讲:我们设计一个估价函数,以任意状态为输入,计算出从该状态到目标状态所需代价的估计值。在搜索中,仍然维护一个堆,不断从堆中取出 当前代价+未来估价 最小的状态进行扩展
- 为了保证第一次从堆中取出目标状态时得到的就是最优解,我们设计的估价函数需要满足一个基本准则:估价函数的估值不能大于未来实际代价,估价比实际代价更优。
- 这种带有估价函数的优先队列BFS就称为A * 算法。只要保证对于任意状态state,都有f(state)≤g(state),A * 算法就一定能在目标状态第一次从堆中被取出时得到最优解,并且在搜索过程中每个状态只需要被扩展一次(之后再被取出就可以直接忽略)。估价f(state)越准确,越接近g(state),A * 算法的效率就越高。如果估价始终为0,就等价于普通优先队列BFS。
- A * 算法提高搜索效率的关键,就在于能否设计出一个优秀的估价函数。估价函数在满足上述设计准则的前提下,含应该尽可能反映未来实际代价的变化趋势和相对大小关系,这样搜索才会较快的逼近最优解
- 估价函数的设计准则:
- 估值f(state)≤未来实际代价g(state)
那么再回到这个题目上面,我们只需要设计出估价函数即可,这个题目的权值是距离,当我们到达一个点后,利用bfs的状态转移可以很容易的知道从起点到当前点的距离,那怎么知道当前点到终点的距离呢?我们可以一开始从终点跑一遍迪杰斯特拉或者spfa,这样就能轻松表达出估价函数了,有了估价函数后,我们在优先队列+bfs的基础上,更改排序函数的机制为估价函数,然后给我们的bfs函数改个名字,就变成A*算法了
有一个坑点需要注意一下,当终点和起点重合的时候,我们需要让k++,以避免出现让起点直接到达终点的现象发生
代码,模板题:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<sstream>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=1e3+100;
int d[N];
bool vis[N];
struct Node
{
int to,w;
Node(int TO,int W)
{
to=TO;
w=W;
}
Node(){}
bool operator<(const Node& a)const
{
return w+d[to]>a.w+d[a.to];
}
};
vector<Node>node1[N],node2[N];//1:正向边 2:反向边
void spfa(int x)
{
memset(vis,false,sizeof(vis));
memset(d,inf,sizeof(d));
queue<int>q;
q.push(x);
d[x]=0;
vis[x]=true;
while(!q.empty())
{
int from=q.front();
q.pop();
vis[from]=false;
for(int i=0;i<node2[from].size();i++)
{
int to=node2[from][i].to;
int w=node2[from][i].w;
if(d[to]>d[from]+w)
{
d[to]=d[from]+w;
if(!vis[to])
{
vis[to]=true;
q.push(to);
}
}
}
}
}
int A_star(int start,int end,int k)
{
priority_queue<Node>q;
q.push(Node(start,0));
while(!q.empty())
{
Node cur=q.top();
q.pop();
if(cur.to==end)
{
k--;
if(!k)
return cur.w;
}
for(int i=0;i<node1[cur.to].size();i++)
{
int to=node1[cur.to][i].to;
int w=node1[cur.to][i].w;
q.push(Node(to,cur.w+w));
}
}
return -1;
}
int main()
{
// freopen("input.txt","r",stdin);
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
{
node1[i].clear();
node2[i].clear();
}
while(m--)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
node1[u].push_back(Node(v,w));
node2[v].push_back(Node(u,w));
}
int s,e,k;
scanf("%d%d%d",&s,&e,&k);
spfa(e);
if(d[s]==inf)
{
printf("-1\n");
continue;
}
if(s==e)//特判一下,坑
k++;
printf("%d\n",A_star(s,e,k));
}
return 0;
}