poj2449 第k短路 (单源最短路径+A*)

4 篇文章 0 订阅
4 篇文章 0 订阅

题目链接:http://poj.org/problem?id=2449

题意
题目的意思很明确,就是让你求s到t的第k短路。不过有一个trick点就是必须要经过路径,也就是说如果s=t的话,在算第k短路时不能算s到t为0这条路。

题解
基本就是裸的第k短路,而第k短路是单源最短路和A*算法的组合。对于A*算法,都知道f(n)=g(n)+h(n),这里h(n)为启发式函数。我们令这里的g(n)为从源点s到n所经过的路径,h(n)为把所有边反向后从终点t到n的最短路径dist[n]。即估值=源点到当前点的距离+当前点到终点的最短距离。这时我们建一个优先队列,每次弹出估值f()最小的点,如果弹出的点是t就计算t出队的次数,如果次数等于k,那么到当前点的距离g()即为答案,否者就拓展与当前点相连的边。

#include <cstdio>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;

const int maxn = 1e3+5;
const int maxm = 1e5+5;
int n,m,s,t,k,ans;
//前向星储存边
int head[maxn],A_head[maxn],cnt1,cnt2;
//dist[]储存终点到其它点的最短路
int dist[maxn];

struct Edge{
    int to,w,next;
}edge1[maxm],edge2[maxm];

struct A_Node{
    int p,g,h;
    bool operator < (A_Node tmp) const
    {
        return tmp.g+tmp.h < g+h;
    }
};

typedef pair<int,int> P;

void init()
{
    memset(head,-1,sizeof(head));
    memset(A_head,-1,sizeof(A_head));
    memset(dist,INF,sizeof(dist));
    cnt1=cnt2=0;
}

void add_edge(int from,int to,int w,bool flag)
{
    if(flag)
    {
        edge1[cnt1].to = to;
        edge1[cnt1].w = w;
        edge1[cnt1].next = head[from];
        head[from] = cnt1++;
    }
    else
    {
        edge2[cnt2].to = to;
        edge2[cnt2].w = w;
        edge2[cnt2].next = A_head[from];
        A_head[from] = cnt2++;
    }
}

void Dijkstra() //优先队列优化的Dijkstra求终点到其它点的最短路
{
    priority_queue<P,vector<P>,greater<P> >pq;
    P p;
    dist[t] = 0;
    pq.push(P(0,t));
    while(!pq.empty())
    {
        p = pq.top(),pq.pop();
        int u = p.second;
        for(int i=head[u];i!=-1;i=edge1[i].next)
        {
            if(dist[edge1[i].to] > dist[u] + edge1[i].w)
            {
                dist[edge1[i].to] = dist[u]+edge1[i].w;
                pq.push(P(dist[edge1[i].to],edge1[i].to));
            }
        }
    }
}

void A_stra()
{
    A_Node cur,next;
    int num=0;
    priority_queue<A_Node>pq;
    //源点为终点,不去掉为0的路径就相当于求第k+1短路
    if(s==t) k++;  
    if(dist[s]==INF)
    {
        ans=-1;
        return;
    }
    cur.p = s;
    cur.g = 0;
    cur.h = dist[s];
    pq.push(cur);
    while(!pq.empty())
    {
        cur = pq.top(),pq.pop();
        if(cur.p==t) num++;
        if(num==k)
        {
            ans = cur.g;
            return;
        }
        for(int i=A_head[cur.p];i!=-1;i=edge2[i].next)
        {
            next.p = edge2[i].to;
            next.g = cur.g+edge2[i].w;
            next.h = dist[next.p];
            pq.push(next);
        }
    }
    ans = -1;
    return;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        int u,v,w;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add_edge(v,u,w,true);
            add_edge(u,v,w,false);
        }
        scanf("%d%d%d",&s,&t,&k);
        Dijkstra();
        A_stra();
        printf("%d\n",ans);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值