【POJ2449】Remmarguts' Date(A*搜索)

题目:我是超链接

题意:裸的K短路(s到t第k短的路)。题目大意就是给出一个图,然后给出一个起点个一个终点,求这两点间的第K短路。

题解:

如何求第k短呢?一种简单的方法是广搜,记录t出队列的次数,第k次出队的时候,就是第k短路,但这样搜索时空复杂度很高

A*是一种常见的优化,启发式搜索,它可以用公式表示为f[n]=g[n]+h[n],f[n]表示从s经节点n到t的估价函数,g[n]是在s到n的实际代价,h[n]是从n到t的最佳路径估计代价。在设计中,要保证h[n]<=n到t的实际代价(宁愿估小了遍历一遍都不要估大了),h[n]越接近真实值,速度++

算法过程:

1.反向连边,目的是求所有点到t的最短路,用dis[i]表示i到t的最短路,其实这就是A*的启发函数,显然:h(n)<= n到t的实际代价。

2.初始化状态。状态中存放当前到达的点i,fi,gi,显然,f[i]=g[i]+dis[i],存入优先队列中

3.状态转移,转移到相邻的点

4.终止条件:每个节点最多入队列k次,当t出队列k次的时候,找到解

代码:

#include <cstdio>
#include <cstring>
#include <queue>
#define INF 1e9
#define M 100005
#define N 1005
using namespace std;
struct hh
{
	int f,g,i;
	bool operator <(const hh &a)const{return a.f<f;}
};
int tot,nxt[M],point[N],v[M],c[M],dis[N],tot1,nxt1[M],point1[N],v1[M],c1[M],s,t,k,cnt[N];
bool vis[N];
void addline(int x,int y,int t){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=t;}
void addline1(int x,int y,int t){++tot1; nxt1[tot1]=point1[x]; point1[x]=tot1; v1[tot1]=y; c1[tot1]=t;}
void spfa()
{
	queue <int> q;
	memset(vis,0,sizeof(vis));
	memset(dis,0x7f,sizeof(dis));
	dis[t]=0;
	q.push(t);
	while (!q.empty())
	{
		int now=q.front(); q.pop(); vis[now]=false;
		for (int i=point1[now];i;i=nxt1[i])
		  if (dis[v1[i]]>dis[now]+c1[i])
		  {
		  	dis[v1[i]]=dis[now]+c1[i];
		  	if (!vis[v1[i]]){q.push(v1[i]); vis[v1[i]]=true;}
		  }
	}
}
int A_star(int s)
{
	priority_queue<hh>q;
    if (dis[s]>INF) return -1;
    memset(vis,0,sizeof(vis));
	q.push((hh){dis[s],0,s});
	while (!q.empty())
	{
		hh now=q.top(),vv; q.pop();
		cnt[now.i]++;
		if (cnt[t]==k) return now.f;
		if (cnt[now.i]>k) continue;
		for (int i=point[now.i];i;i=nxt[i])
		{
		    vv.i=v[i]; vv.g=now.g+c[i]; vv.f=vv.g+dis[v[i]];
		  	q.push(vv);
		}
	}	
	return -1;
}
int main()
{
	int m,n,i;
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++)
	{
		int x,y,t;
		scanf("%d%d%d",&x,&y,&t);
		addline1(y,x,t);
		addline(x,y,t);
	}
	scanf("%d%d%d",&s,&t,&k);
	if (s==t) k++;
	spfa();
	printf("%d",A_star(s));
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值