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;
}

918

被折叠的 条评论
为什么被折叠?



