第K短路问题,可以用A*来写,我了个去,遇到了超级令人无语的错误...
如何求第K短呢?有一种简单的方法是广度优先搜索,记录t出队列的次数,当t第k次出队列时,就是第k短路了。但点数过大时,入队列的节点过多,时间和空间复杂度都较高。
现在有另一种更加高效的算法A*。
介绍一些关键的概念:
首先,对于状态空间搜索,是可以提出一个通用的搜索算法框架的,而这个框架中则主要使用了open,和closed两个表。open表,保存了当前待扩展节点,closed表则保存已扩展节点,而BFS DFS也可以纳入这个框架,比如DFS中使用的white black gray染色,实际于 open closed便是对应的。
对于启发式搜索一般具有如下评估函数f*(n) = g*(n)+h*(n),f*(n)表示从起始点经过n到达目标的估计函数。
g*(n)表示从起始点到达节点n的花费函数,比如对于最短路问题,可能就是已经发现的从出发点,到当前节点的最小cost,h*(n)表示从当前状态n到目标状态的估计花费。
对于h*(n)施加如下约束,g*(n) >= g(n) ,h*(n) <= h(n),即估计花费从是小于等于实际最小花费,则启发式搜索就是A*算法。而A*算法是可接纳的,也就是说如果存在一条到达目标的最优路径,A*算法可以保证找到这条路径。
继续对A*算法的h函数施加约束,可以保证满足一致性,或者叫单调性,约束是:对于任意的状态ni,nj
h*(ni) - h*(nj) <= c(ni,nj)
一致性可以保证,当 A*扩展节点n时,它已经找到了到达节点n的最优路径。
相关资料:
http://duanple.blog.163.com/blog/static/70971767200811344425606/
http://blog.csdn.net/bmexue/article/details/6967027
http://www.doc88.com/p-34182603434.html
http://blog.csdn.net/emilmatthew/article/details/1338808
另外:启发式搜索有一个明显的缺点,就是以空间换取时间,可能会MLE,如果估价函数写的不好的话,可能速度快,但没有那么准确(估价系数是1.0是是最准确的,如果2.0、3.0可能速度快,但可能会影响结果),这就需要大家根据具体情况再考虑。
AC CODE:
CODE:
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define MAXM 100010
#define MAXN 1010
#define INF 0x3f3f3f3f
struct Edge
{
int v, g, f;
int next;
bool operator < (const Edge &a) const //结构体重载,如果在外面重载会MLE
{
if(a.f == f) return a.g < g; //f = h + g, 此时h为t->s的最短路径。
else return a.f < f;
}
}edge[MAXM], edge2[MAXM];
/*bool operator < (const node a, const node b) //这样重载会MLE,不知为啥
{
if(a.f == b.f) return a.g < b.g;
else return a.f < b.f;
}*/
int n, m, cnt;
int first[MAXN], first2[MAXN];
int d[MAXN], ct[MAXN];
void init()
{
cnt = 0;
memset(first, -1, sizeof(first));
memset(first2, -1, sizeof(first2));
memset(ct, 0, sizeof(ct));
}
void read_graph(int u, int v, int w)
{
edge[cnt].v = v, edge[cnt].g = w;
edge[cnt].next = first[u], first[u] = cnt;
edge2[cnt].v = u, edge2[cnt].g = w;
edge2[cnt].next = first2[v], first2[v] = cnt++;
} //建立正、反向图。
void spfa(int src)
{
queue<int> q;
bool inq[MAXN] = {0};
for(int i = 1; i <= n; i++) d[i] = (i == src)?0:INF;
q.push(src);
while(!q.empty())
{
int x = q.front(); q.pop();
inq[x] = 0, ct[x]++;
if(ct[x] >= n) return ; //存在负环
for(int e = first2[x]; e != -1; e = edge2[e].next)
{
int v = edge2[e].v, w = edge2[e].g;
if(d[v] > d[x] + w)
{
d[v] = d[x] + w;
if(!inq[v])
{
inq[v] = 1;
q.push(v);
}
}
}
}
}
int A_STAR(int s, int t, int k)
{
int tot = 0;
struct Edge cur, now;
priority_queue<Edge> Q;
if (s == t) k++;
if (d[s] == INF) return -1; //没有路径可达
cur.v = s, cur.g = 0, cur.f = d[s];
Q.push(cur);
while(!Q.empty())
{
cur = Q.top(); Q.pop();
if(cur.v == t)
{
tot++;
if(tot == k) return cur.g;
}
for(int e = first[cur.v]; e != -1; e = edge[e].next)
{
int v = edge[e].v, g = edge[e].g;
now.v = v, now.g = cur.g + g;
now.f = now.g + d[v]; //更新g
Q.push(now);
}
}
return -1;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
read_graph(u, v, w);
}
int s, e, k;
scanf("%d%d%d", &s, &e, &k);
spfa(e);
int ans = A_STAR(s, e, k);
printf("%d\n", ans);
}
return 0;
}
MLE CODE:
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define MAXM 100005
#define MAXN 1005
#define INF 0x3f3f3f3f
struct Edge
{
int v, g, f;
int next;
/*bool operator < (const Edge &a) const //结构体重载,如果在外面重载会MLE
{
if(a.f == f) return a.g < g; //f = h + g, 此时h为t->s的最短路径。
else return a.f < f;
}*/
}edge[MAXM], edge2[MAXM];
bool operator < ( Edge a, Edge b) //这样重载会MLE,不知为啥
{
if(a.f == b.f) return a.g < b.g;
else return a.f < b.f;
}
int n, m, cnt;
int first[MAXN], first2[MAXN];
int d[MAXN], ct[MAXN];
void init()
{
cnt = 0;
memset(first, -1, sizeof(first));
memset(first2, -1, sizeof(first2));
memset(ct, 0, sizeof(ct));
}
void read_graph(int u, int v, int w)
{
edge[cnt].v = v, edge[cnt].g = w;
edge[cnt].next = first[u], first[u] = cnt;
edge2[cnt].v = u, edge2[cnt].g = w;
edge2[cnt].next = first2[v], first2[v] = cnt++;
} //建立正、反向图。
void spfa(int src)
{
queue<int> q;
bool inq[MAXN] = {0};
for(int i = 1; i <= n; i++) d[i] = (i == src)?0:INF;
q.push(src);
while(!q.empty())
{
int x = q.front(); q.pop();
inq[x] = 0, ct[x]++;
if(ct[x] >= n) return ; //存在负环
for(int e = first2[x]; e != -1; e = edge2[e].next)
{
int v = edge2[e].v, w = edge2[e].g;
if(d[v] > d[x] + w)
{
d[v] = d[x] + w;
if(!inq[v])
{
inq[v] = 1;
q.push(v);
}
}
}
}
}
int A_STAR(int s, int t, int k)
{
int tot = 0;
struct Edge cur, now;
priority_queue<Edge> Q;
if (s == t) k++;
if (d[s] == INF) return -1; //没有路径可达
cur.v = s, cur.g = 0, cur.f = d[s];
Q.push(cur);
while(!Q.empty())
{
cur = Q.top(); Q.pop();
if(cur.v == t)
{
tot++;
if(tot == k) return cur.g;
}
for(int e = first[cur.v]; e != -1; e = edge[e].next)
{
int v = edge[e].v, g = edge[e].g;
now.v = v, now.g = cur.g + g;
now.f = now.g + d[v]; //更新g
Q.push(now);
}
}
return -1;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
read_graph(u, v, w);
}
int s, e, k;
scanf("%d%d%d", &s, &e, &k);
spfa(e);
int ans = A_STAR(s, e, k);
printf("%d\n", ans);
}
return 0;
}