POJ 2449 Remmarguts' Date K最短路问题(单源点最短路径+A*算法)

算法解释感谢Jarily  http://blog.csdn.net/jarily/article/details/8871968

/*
 *算法引入:
 *在单源点最短路径问题中,实际运用时还需知道最短路径外,次短路或者第三短路;
 *即要知道多条最短路,并排出其长度增加的顺序,即为K最短路问题;
 *
 *算法思想:
 *单源点最短路径+高级搜索A*;
 *A*算法结合了启发式方法和形式化方法;
 *启发式方法通过充分利用图给出的信息来动态地做出决定而使搜索次数大大降低;
 *形式化方法不利用图给出的信息,而仅通过数学的形式分析;
 *
 *算法通过一个估价函数f(h)来估计图中的当前点p到终点的距离,并由此决定它的搜索方向;
 *当这条路径失败时,它会尝试其他路径;
 *对于A*,估价函数=当前值+当前位置到终点的距离,即f(p)=g(p)+h(p),每次扩展估价函数值最小的一个;
 *
 *对于K短路算法来说,g(p)为当前从s到p所走的路径的长度;h(p)为点p到t的最短路的长度;
 *f(p)的意义为从s按照当前路径走到p后再走到终点t一共至少要走多远;
 *
 *为了加速计算,h(p)需要在A*搜索之前进行预处理,只要将原图的所有边反向,再从终点t做一次单源点最短路径就能得到每个点的h(p)了;
 *
 *算法步骤:
 *(1),将有向图的所有边反向,以原终点t为源点,求解t到所有点的最短距离;
 *(2),新建一个优先队列,将源点s加入到队列中;
 *(3),从优先级队列中弹出f(p)最小的点p,如果点p就是t,则计算t出队的次数;
 *如果当前为t的第k次出队,则当前路径的长度就是s到t的第k短路的长度,算法结束;
 *否则遍历与p相连的所有的边,将扩展出的到p的邻接点信息加入到优先级队列;
 *
 *算法测试:
 *PKU2449(Remmarguts' Date)
 *
 *题目大意:
 *求从s到t的第k短路的长度;
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define N 1010
#define M 100010
#define INF 1e9

typedef int type;
type dis[N];
int out[N];
bool visit[N];
struct Node
{
    int pos;
    type g,f; // g(i) 为从start到i目前已走的距离  ,  f(i)=g(i)+dis(i)
    friend bool operator<(Node a,Node b)
    {
        return a.f>b.f;
    }
};
struct
{
    int to,next;
    type c;
}edge[M<<1];
int ip;
int head1[N],head2[N];
void addedge1(int u,int v,type c)
{
    edge[ip].to=v; edge[ip].c=c; edge[ip].next=head1[u]; head1[u]=ip++;
}
void addedge2(int u,int v,type c)
{
    edge[ip].to=v; edge[ip].c=c; edge[ip].next=head2[u]; head2[u]=ip++;
}

bool spfa(int start,int end,int n) //以end为起点求i到end的最短路
{
    queue<int>q;
    memset(out,0,sizeof(out));
    memset(visit,0,sizeof(visit));
    for(int i=0;i<=n;i++) dis[i]=INF;
    q.push(start); visit[start]=1; dis[start]=0;
    int top;
    while(!q.empty())
    {
        top=q.front(); q.pop(); visit[top]=0; out[top]++;
        if(out[top]>n) return 0;
        for(int p=head2[top];p!=-1;p=edge[p].next)
        {
            if(dis[ edge[p].to ] > dis[top] + edge[p].c )
            {
                dis[edge[p].to]=dis[top]+edge[p].c;
                if(!visit[edge[p].to])
                {
                    visit[edge[p].to]=1; q.push(edge[p].to);
                }
            }
        }
    }
    if(dis[end]==INF) return 0;
    return 1;
}

int K_load(int start,int end,int k)
{
    Node top,temp;
    priority_queue<Node>q;
    int time=0;
    top.pos=start; top.g=0; top.f=dis[start]; q.push(top);
    while(!q.empty())
    {
        top=q.top(); q.pop();
        if(top.pos==end) time++;
        if(time==k) return top.f;
        for(int p=head1[top.pos];p!=-1;p=edge[p].next)
        {
            temp.pos=edge[p].to;
            temp.g=top.g+edge[p].c;
            temp.f=temp.g+dis[edge[p].to];
            q.push(temp);
        }
    }
    return -1;
}

int solve(int start,int end,int k,int n)
{
    if(!spfa(end,start,n)) return -1;
    return K_load(start,end,k);
}

int main()
{
    int n,m;
    int x,y;
    int start,end,k;
    type c;
    while(cin>>n>>m)
    {
        ip=0;
        memset(head1,-1,sizeof(head1));
        memset(head2,-1,sizeof(head2));
        while(m--)
        {
            scanf("%d%d%d",&x,&y,&c);  //c输入类型需要修改
            addedge1(x,y,c);
            addedge2(y,x,c);  //建反向边
        }
        cin>>start>>end>>k;
        if(start==end) k++;   //这题需要特殊处理
        cout<<solve(start,end,k,n)<<endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值