【POJ】2449 Remmarguts' Date k短路(最短路径+A*)

13 篇文章 0 订阅
11 篇文章 0 订阅

题目传送门

题目大意就是给出起点s,终点t和一个参数k,以及一张图,问s到t间的k短路。

对于所有k短路问题,我们都可以用最短路+A*的方法来解决。

首先,我们根据输入数据建两张图:一张是和输入数据边的方向相同的正图,另一张是和输入数据边的方向相反的反图。

我们对反图以终点t为起点做一遍最短路(我用的是SPFA),可以得到所有点到终点t的最短距离。

然后从起点s开始做一遍A*,第k次遍历到终点t时的路径长度就是答案。

其中我们定义A*的估价函数f(i)=g(i)+h(i),h(i)的大小就是最短路得出来的节点i到终点t的最短路径长度,g(i)是原点s到节点i的实际距离。

为了使f较小的节点会被较早的扩展,保持BFS的特性,我们可以用一个优先队列来维护,以f为关键字。

因为我们每次取队头节点p,也就是整个队列中f最小的节点,所以当前状态是队列里所有节点p的关键字f最小的状态,在把所有与节点p相连的节点插入队列,相当于枚举了所有节点的关键字f。每次取的都是所有状态中关键字f最小的状态,保证了每次枚举到的节点都是以最短路径扩展而来。因此可以推出用A*的方法得到的k短路是成立的。

最后一次搜到终点t时,g(t)就是答案。


注意:如果s=t,则++k。这个……我也不知道为何……

附上AC代码:

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

struct note{
	int d,w;
};
vector <note> z[1010],f[1010];
int n,m,x,y,w,s,t,k,dis[1010];
queue <int> que;
bool b[1010];
struct node{
	int v,g,f;
	bool operator < (const node lyf) const {
		if (lyf.f==f) return lyf.g<g;
		return lyf.f<f;
	}
};
priority_queue <node> dui;

void spfa(){
	for (int i=1; i<=n; ++i) dis[i]=2e9;
	que.push(t),b[t]=1,dis[t]=0;
	while (!que.empty()){
		int p=que.front();que.pop(),b[p]=0;
		for (int i=0; i<f[p].size(); ++i)
			if (dis[f[p][i].d]>dis[p]+f[p][i].w){
				dis[f[p][i].d]=dis[p]+f[p][i].w;
				if (!b[f[p][i].d]){
					b[f[p][i].d]=1;
					que.push(f[p][i].d);
				}
			}
	}
	return;
}

int astar(){
	if (s==t) ++k;
	if (dis[s]==2e9) return -1;
	int c=0;
	dui.push((node){s,0,dis[s]});
	while (!dui.empty()){
		node p=dui.top();dui.pop();
		if (p.v==t) ++c;
		if (c==k) return p.g;
		for (int i=0; i<z[p.v].size(); ++i)
			dui.push((node){z[p.v][i].d,p.g+z[p.v][i].w,p.g+z[p.v][i].w+dis[z[p.v][i].d]});
	}
	return -1;
}

int main(void){
	scanf("%d%d",&n,&m);
	for (int i=1; i<=m; ++i){
		scanf("%d%d%d",&x,&y,&w);
		z[x].push_back((note){y,w});
		f[y].push_back((note){x,w});
	}
	scanf("%d%d%d",&s,&t,&k);
	spfa();
	printf("%d",astar());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值