POJ - 2449 Remmarguts' Date(第k短路:spfa+A*)

本文深入解析A*算法,一种结合优先队列BFS与估价函数的高效搜索算法,适用于求解最短路径问题。文章通过实例讲解算法原理,包括估价函数设计与优化,以及在有向图中寻找第k短路的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点击查看

题目大意:给出一个有向图,求第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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Frozen_Guardian

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

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

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

打赏作者

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

抵扣说明:

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

余额充值