第k短路 Remmarguts' Date-Astar算法实现

题目传送门:

http://poj.org/problem?id=2449

题目简述:

给定 N N N点, M M M边,求 S S S T T T的第 k k k短路的长度,允许重复经过点或边。
其实,题意上不允许不走,即长度为0不属于一条路径。

题解:

根据 A s t a r Astar Astar,我们主要是通过 f [ i ] f[i] f[i],即预测未来到达终点要消耗的代价,以及现在已经消耗的代价 h [ i ] h[i] h[i] ,令 c [ i ] = f [ i ] + h [ i ] c[i]=f[i]+h[i] c[i]=f[i]+h[i],利用优先队列,优先扩展 c [ ] c[] c[]较小的点,这就是 A s t a r Astar Astar的精髓。
A s t a r A_star Astar算法详情可看《算法竞赛进阶指南——李煜东》
那么本题可将当前点到终点的最短路作为 f [ ] f[] f[],预处理为一次 s p f a spfa spfa,我们知道,在BFS中,某状态第一次从堆中被取出时,就得到了起点到它的最小代价。由数学归纳法:“当第i次从堆中取出节点 x x x时,对应的代价即为 S S S x x x的第i短路”

上代码:

#include<cstdio>
#include<queue>
using namespace std;
int f[1010],first1[1010],first2[1010],len1=0,len2=0,n,m,k;
struct bian1{int y,next,c;}a[200100];
struct bian2{int y,next,c;}b[200100];
//f[i]表示估值函数,存i到终点的最短路保证了f[i]<=g[i](其他路径) 
void ins1(int x,int y,int c)
{
	len1++; a[len1].y=y; a[len1].c=c;
	a[len1].next=first1[x]; first1[x]=len1; 
}
void ins2(int x,int y,int c)
{
	len2++; b[len2].y=y; b[len2].c=c;
	b[len2].next=first2[x]; first2[x]=len2; 
}
queue<int> q;
int v[1010],st,ed;
void spfa()
{
	for(int i=1;i<=n;i++) f[i]=999999999;
	q.push(ed);
	f[ed]=0; v[ed]=1; 
	while(!q.empty())
	{
		int x=q.front(); q.pop(); v[x]=0;
		for(int i=first1[x];i>0;i=a[i].next)
		{
			int y=a[i].y;
			if(f[y]>f[x]+a[i].c)
			{
				f[y]=f[x]+a[i].c;
				if(!v[y]) 
				{
					v[y]=1;
					q.push(y); 
				}
			}
		}
	}
}
struct node{
	int id,c,h;//c表f+h h表已走 
	friend bool operator < (const node &x,const node &y)
    { if(x.c==y.c) return x.h>y.h; return x.c>y.c; }
};
node cur;
priority_queue<node> p;
int a_star()
{
	if(st==ed) k++;//不允许不走 
	if(f[st]==999999999) return -1;
	for(int i=1;i<=n;i++) v[i]=0;
	cur.id=st;cur.c=f[st],cur.h=0;
	p.push(cur); 
	node x;
	while(!p.empty())
	{
		x=p.top(); p.pop(); 
		v[x.id]++; if(v[ed]==k) return x.c;//从堆取出 
		for(int i=first2[x.id];i>0;i=b[i].next) 
		{
			int y=b[i].y;
			if(v[y]<k)
			{	
				cur.id=y,cur.c=x.h+f[y]+b[i].c,cur.h=x.h+b[i].c;
				p.push(cur);
			} 
		}
	}return -1; 
}
int main()
{
	int x,y,c;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&c);
		ins1(y,x,c); ins2(x,y,c); 
	}scanf("%d %d %d",&st,&ed,&k);
	spfa();	//for(int i=1;i<=n;i++) printf("%d ",f[i]);
	printf("%d",a_star());
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值