[POJ 2449] Remmarguts' Date [A*搜索]

20 篇文章 0 订阅
17 篇文章 0 订阅

求第k最短路问题,有向图,数据范围:点数不超过1000,边数不超过10^5,k不超过1000

题目需要注意的地方:虽然题目描述说的是求T到S的第k短路,但是实际上根据样例可以知道我们应该求从S到T的第k短路...另外如果S==T,最初的那个不用走就到了的是不算做一条路的..也就是说必须要走

算法:首先用原图的反向图计算每个点到T的最短路,然后使用A*算法,估价函数f(i)=g(i)+h(i),其中h(i)是从i到T的最短路长度,g(i)是目前从起点到i所搜索出来的某条路径的长度。使用优先队列,每次选取f(i)最小的点进行扩展。一个点可以在同一时刻在队列里出现多次,因为他们代表不从S按照不同的路径到达这个点。

空间复杂度:队列的大小不会超过nk,因为如果某个点在队列里累计出现了k+1次,那么从k+1次的那个点向外扩展到t,最少是第k+1短路而不是k短路

时间复杂度:会遍历到从S点开始所有长度小于第k短路的有希望到达T点的路,若这些路上的点的和为t,则复杂度为tlogt。因为每个点都不会进入队列超过k次,所以复杂度不会超过nklog(nk)

需要注意的地方:只有出队序列才是满足估价函数逐渐增长的,不可以按照入队序列查看,T点的第k次出队才是第k短路。如果某个点不能够到达T点,那么我们不会将其加入优先队列。若队列为空,则不存在第k短路。

#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

struct Node {
	int fe,v;
	bool visited;
};
struct Edge {
	int ne,t,v;
};
struct Que {
	int i,f;
	Que() {}
	Que(int ii,int ff) {
		i=ii;f=ff;
	}
	friend bool operator < (const Que &a,const Que &b) {
		return a.f>b.f;
	}
};

int n,m,k,s,t,p,q,ansk;
Node a[1001];
Edge b[100001];
Node c[1001];
Edge d[100001];
int que[1001];
priority_queue <Que> e;

void putedge(int x,int y,int z) {
	b[p].ne=a[x].fe;
	b[p].t=y;
	b[p].v=z;
	a[x].fe=p;
	d[p].ne=c[y].fe;
	d[p].t=x;
	d[p].v=z;
	c[y].fe=p;
	p++;
}

void spfa(int s,int t,Node a[],Edge b[]) {
	int i,j;
	p=q=0;
	a[s].v=1;
	a[s].visited=true;
	que[q++]=s;
	while (p!=q) {
		i=que[p];
		for (j=a[i].fe;j;j=b[j].ne) {
			if (a[b[j].t].v==0||a[b[j].t].v>a[i].v+b[j].v) {
				a[b[j].t].v=a[i].v+b[j].v;
				if (!a[b[j].t].visited) {
					a[b[j].t].visited=true;
					que[q]=b[j].t;
					q=(q+1)%1001;
				}
			}
		}
		a[i].visited=false;
		p=(p+1)%1001;
	}
}

int astar(int s,int t,Node a[],Edge b[]) {
	e.push(Que(s,a[s].v));
	while (!e.empty()) {
		int i=e.top().i;
		int v=e.top().f;
		//printf("%d %d\n",i,v-a[i].v);
		e.pop();
		if (i==t) {
			ansk++;
			if (k==ansk) return v;
		}
		for (int j=a[i].fe;j;j=b[j].ne) {
			if (a[b[j].t].v>=0) {
				e.push(Que(b[j].t,v+b[j].v+a[b[j].t].v-a[i].v));
				//printf("Push: %d %d\n",b[j].t,v-a[i].v+b[j].v);
			}
		}
	}
	return -1;
}

int main() {
	int i,x,y,z;
	scanf("%d%d",&n,&m);
	p=1;
	ansk=0;
	memset(a,0,sizeof(a));
	memset(c,0,sizeof(c));
	for (i=0;i<m;i++) {
		scanf("%d%d%d",&x,&y,&z);
		putedge(x,y,z);
	}
	scanf("%d%d%d",&s,&t,&k);
	if (s==t) k++;
	spfa(t,s,c,d);
	for (i=1;i<=n;i++) a[i].v=c[i].v-1;
	//for (i=1;i<=n;i++) printf("%d ",a[i].v);
	//printf("\n");
	if (a[s].v!=-1) {
		printf("%d\n",astar(s,t,a,b));
	} else printf("-1\n");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值