POJ - 2449 K短路

A*+spfa

一张图,找K短路

转自:http://blog.csdn.net/mbxc816/article/details/7197228

“描述一下怎样用启发式搜索来解决K短路。

首先我们知道A*的基础公式:f(x)=g(x)+h(x);对h(x)进行设计,根据定义h(x)为当前的x点到目标点t所需要的实际距离。也就是说x->t距离,由于有很多的节点都是到t的距离,为了计算这个估计值,当然必须先算出x->t的最短路径长度。显然x的值很多而t的值只有一个,对每个x去求单源点最短路径当然不划算!于是反过来做,从t点出发到其他点的单源点最短路径,这样吧估价函数h(x)都求出来,注意这样求出来的h(x)=h*(x);

然后就可以对构造完的h(x)开始启发式搜索了。

首先的点当然就是定义头结点了,头结点的已消耗代价为0,估计代价为h[s],下一个点为v;进入队列,开始for循环。每次取出队头的f(x)最小的节点对其他节点进行拓展。对当前节点的拓展次数++,若当前节点的拓展次数超过K,显然不符合要求,则不进行拓展。若对t节点的拓展次数恰好为K,则找到了所需要的。对当前节点的拓展次数即为到当前节点的第几短路。找到需要节点的K短路后,返回g(t)即可,也就是通过K次拓展的实际消耗的长度。

在for循环中的入队情况:当前节点的可拓展所有边,的所有状态都入队,当前节点到拓展节点的实际代价为当前节点的实际代价+两节点之间的边长。下个节点就是拓展节点,估计函数的值则为拓展节点到目标节点的距离h(x);”



这个题目是单向边,所以有一点点麻烦,

f = g + h;

g 是从起点到当前点的距离

h 是从当前点到尾点的距离

首先我们就要把 h 求出来,就是最短路,从结尾点开始,跑spfa

求出到每个点的最短距离。这样 h 就出来了

因为是从结尾点开始搜,所以要倒着建边。

而顺着建边是为了搜 g,,


// Layout.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<cstdio>
#include<queue>
using namespace std;
const int N = 1009;
const int INF = 0x3f3f3f3f;

struct node
{
	int v, w,next;
};
int sign,head[N],dis[N],st,en,k,rehead[N];
node p[200500],rep[200500];

void add(int x, int y, int z)
{
	sign++;
	p[sign].next = head[x];
	p[sign].v = y; 
	p[sign].w = z;
	head[x] = sign;
	rep[sign].next = rehead[y];
	rep[sign].v = x;
	rep[sign].w = z;
	rehead[y] = sign;
	return;
}

void spfa(int x,int *d)
{
	int u, v;
	bool mark[N];
	memset(mark, 0, sizeof(mark));
	int l, r;
	queue<int>q;
	d[x] = 0;
	mark[x] = 1;
	q.push(x);

	while (!q.empty())
	{
		u = q.front();
		q.pop();
		mark[u] = 0;
		for (int i = rehead[u]; i; i = rep[i].next)
		{
			v = rep[i].v;
			if (d[v] > d[u] + rep[i].w)
			{
				d[v] = d[u] + rep[i].w;
				if (!mark[v])
				{
					mark[v] = 1;
					q.push(v);
				}
			}
		}
	}

	return;
}

struct edge
{
	int pos;
	int h, g;
	bool operator < (const edge & a) const
	{
		return a.h + a.g < h + g;
	}
};

int Astar()
{ 
	int cnt = 0;
	if (st == en) k++;  //要多加一次,首尾点一样。一开始就是尾点。
	if (dis[st] == INF) return -1;

	priority_queue<edge>q;
	edge now, nxt;
	now.pos = st;
	now.g = 0;
	now.h = dis[st];
	q.push(now);
	while (!q.empty())
	{
		nxt = q.top();
		q.pop();
		if (nxt.pos == en) //因为一开始就是尾点,所以cnt加一,而cnt不该加一
		{
			cnt++;
			if (cnt == k) return nxt.g;
		}
		for (int i = head[nxt.pos]; i; i = p[i].next)
		{
			now.pos = p[i].v;
			now.g = nxt.g + p[i].w;
			now.h = dis[p[i].v];
			q.push(now);
		}
	}



	return -1;
}


int main()
{ 
	int  m,  n, x, y, z;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d%d", &x, &y, &z);
		add(x, y, z);
		//add(y, x, z);
	}
	scanf("%d%d%d", &st, &en, &k);
	memset(dis, INF, sizeof(dis));
	
	spfa(en, dis);

	printf("%d\n", Astar());
    return 0;
}




猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/79054613