第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://related.doc88.com/related%2F%E5%90%AF%E5%8F%91%E5%BC%8F%E6%90%9C%E7%B4%A2%5E492273006798%5E20120613094619_kLNtuj8u.html%5E75

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 <iostream>
#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, -1sizeof(first));
    memset(first2, -1sizeof(first2));
    memset(ct, 0sizeof(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 <iostream>
#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, -1sizeof(first));
    memset(first2, -1sizeof(first2));
    memset(ct, 0sizeof(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;
}

 

posted on 2012-10-22 22:37  有间博客  阅读(224)  评论(0编辑  收藏  举报