POJ 2449 Remmarguts' Date (SPFA + A星算法) - from lanshui_Yang

        题目大意:给你一个有向图,并给你三个数s、t 和 k ,让你求从点 s 到 点 t 的第 k 短的路径。如果第 k 短路不存在,则输出“-1” ,否则,输出第 k 短路的长度。

        解题思路:这道题是一道简单的启发式搜索题目。而启发式搜索中A星算法是比较好理解的。A星算法中需要用到一个估价函数:f(n) = g(n)+ h(n)。其中,g(n)是当前量,h(n)是估计量,两者之和 f(n) 是估计值 。在这道题中,g(n)是从起点 s 到 点n 的已走距离,h(n)是从点n 到终点 t 的最短距离(dis[ n ]) 。每当我们走到一个点 tn 时 ,就计算出此时 tn 的g(tn) 、 h(tn)和 f(tn),把 tn 的这些信息压入一个队列,然后下一步选取队列中 f 值最小的节点作为下一步搜索的起点,如此反复,当我们第k次搜索到终点 t 时 ,这时g(t)的值就是我们要求的值。

        Ps:这里我们需要用到优先队列,并且要注意:因为必须离开 s 点再返回,所以,当 s == t 时 ,k ++ 。

请看代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<cstdio>
#include<queue>
#define mem(a , b) memset(a , b , sizeof(a))
using namespace std ;
inline void RD(int &a)
{
    a = 0 ;
    char t ;
    do
    {
        t = getchar() ;
    }
    while (t < '0' || t > '9') ;
    a = t - '0' ;
    while ((t = getchar()) >= '0' && t <= '9')
    {
        a = a * 10 + t - '0' ;
    }
}
inline void OT(int a)
{
    if(a >= 10)
    {
        OT(a / 10) ;
    }
    putchar(a % 10 + '0') ;
}
const int N = 1005 ;
const int M = 1e5 + 5 ;
const int INF = 0x7fffffff ;
struct Edge
{
    int adj ;
    int d ;
    int next ;
} E[M * 3] ;
int head[N] ;
int headN[N] ; // 反向图
int dis[N] ;
bool inq[N] ;
int s , t , k ;
int n , m ;
int ce ;
void chu()
{
    mem(head , - 1) ;
    mem(headN , -1) ;
    mem(inq , 0) ;
    ce = 0 ;
}
void init()
{
    chu() ;
    int i ;
    for(i = 0 ; i < m ; i ++)
    {
        int a , b , c ;
        scanf("%d%d%d" , &a , &b , &c) ;
        ce ++ ;
        E[ce].adj = b ;
        E[ce].d = c ;
        E[ce].next = head[a] ;
        head[a] = ce ;

        ce ++ ;
        E[ce].adj = a ;
        E[ce].d = c ;
        E[ce].next = headN[b] ;
        headN[b] = ce ;
    }
    scanf("%d%d%d" , &s , &t , &k) ;
}
queue<int> q ;
void spfa()  // 计算 dis
{
    while (!q.empty()) q.pop() ;
    q.push(t) ;
    inq[t] = true ;
    int v ;
    while (!q.empty())
    {
        v = q.front() ;
        q.pop() ;
        inq[v] = false ;
        int i ;
        for(i = headN[v] ; i != -1 ; i = E[i].next)
        {
            int tv = E[i].adj ;
            int td = E[i].d ;
            if(dis[v] + td < dis[tv])
            {
                dis[tv] = dis[v] + td ;
                if(!inq[tv])
                {
                    q.push(tv) ;
                    inq[tv] = true ;
                }
            }
        }
    }
}
struct F
{
    int Node ;
    int f ;
    int g ;
    int h ;
};
struct cmp
{
    bool operator () (F a , F b)
    {
        if(a.f == b.f)
        {
            return a.g > b.g ;
        }
        return a.f > b.f ;
    }
} ;
priority_queue<F, vector<F> , cmp> mq ;
int SP()  // A星 算法
{
    if(s == t) k ++ ;
    while (!mq.empty()) mq.pop() ; // 注意队列要清空
    int cnt = 0 ;
    F u ;
    u.Node = s ;
    u.g = 0 ;
    u.h = dis[s] ;
    u.f = u.g + u.h ;
    mq.push(u) ;
    while (!mq.empty())
    {
        F v = mq.top() ;
        if(v.Node == t)
        {
            cnt ++ ;
            if(cnt == k)
            {
                return v.g ;
            }
        }
        mq.pop() ;
        int i ;
        for(i = head[v.Node] ; i != -1 ; i = E[i].next)
        {
            int tv = E[i].adj ;
            int td = E[i].d ;
            F tmp ;
            tmp.Node = tv ;
            tmp.g = v.g + td ;
            tmp.h = dis[tv] ;
            tmp.f = tmp.g + tmp.h ;
            mq.push(tmp) ;
        }
    }
    return -1 ; // 注意:因为此图是有向图,所以K短路可能不存在!!!
}
void solve()
{
    int i ;
    for(i = 1 ; i <= n ; i ++)
    {
        dis[i] = INF ;
    }
    dis[t] = 0 ;
    spfa() ;
    if(dis[s] == INF)
    {
        puts("-1") ;
        return ;
    }
    else
    {
        printf("%d\n" , SP()) ;
    }
}
int main()
{
    while (scanf("%d%d" , &n , &m) != EOF)
    {
        init() ;
        solve() ;
    }
    return 0 ;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值