Acwing 178.第k短路【A*算法】

1.题目
题目链接:点这儿!
2.解决方法
A*算法用来求高复杂度的最短路径问题;本题要求第K短路,这就要求将从起点到终点的所有路径中选出第K短的;我们不可能去把从起点到终点的所有路径一一找出来,那样复杂度太高了;所以我们采用启发式搜索的搜索模式:利用一个估价函数g()作为“启发函数”。

本题中将从终点出发的单源最短路作为估价函数值,这样,每个点在被从起点出发遍历时,都有一个预先知道的距离终点的最短路径长度,可以优先顺着这些点遍历,可以确定最终会遍历到终点;并且,不止一条路径会抵达终点,抵达的先后次序也就是路径的长短;至此,我们发现可以记录从起点向终点搜索的过程中每个点被遍历的次数,这个次数会不断更新;如果这个点是终点,那么记录的就是终点被遍历过的次数,当被更新了K次时,第K次遍历到终点的哪条路径就是第K短路径。

3.代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

//A*算法:先计算出每个点的估价函数(从终点向所有点扩散求最短路,dijkstra)此时估计函数值就是该点到终点的最短距离
//然后从起点开始,根据真实值+估价函数值做堆排序,这样能够保证队列中存储的是图中所有点到终点的最小估计值,以此扩散至终点
//A*算法的核心就是估价函数,本题中为在终点使用dijkstra得到的终点到其余所有点的最短距离dist

#define x first
#define y second

using namespace std;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;

const int MAXN=1010,MAXM=2e5+10;

int h[MAXN],rh[MAXN],ne[MAXM],e[MAXM],w[MAXM],idx;
int n,m,a,b,l,S,T,K,cnt[MAXN];//cnt数组用来记录A*寻找第k短路中每个点被经过的次数
int dist[MAXN];
bool st[MAXN];

void add(int h[],int a,int b,int l)
{
    e[idx]=b;
    ne[idx]=h[a];
    w[idx]=l;
    h[a]=idx++;
}

void dijkstra()//从终点开始求一次单源最短路,在A*中做估价函数值
{
    memset(dist,0x3f,sizeof(dist));
    dist[T]=0;
    
    priority_queue<PII,vector<PII>,greater<PII> > heap;
    heap.push({0,T});
    
    while(!heap.empty()){
        
        auto t=heap.top();
        heap.pop();
        
        int ver=t.y,distance=t.x;
        if(st[ver]) continue;
        st[ver]=true;
        
        for(int i=rh[ver];i!=-1;i=ne[i]){
            int j=e[i];
            if(dist[j]>distance+w[i]){
                dist[j]=distance+w[i];
                heap.push({dist[j],j});
            }
        }
        
    }
}

int astar()
{
    priority_queue<PIII,vector<PIII>,greater<PIII> > heap;
    heap.push({dist[S],{0,S}});//{估价函数,{距起点的实际距离,点编号}} 估价函数=从终点dijkstra到每个点的最短路径+距起点的实际路径
    
    while(!heap.empty()){
        
        auto t=heap.top();
        heap.pop();
        
        int ver=t.y.y,distance=t.y.x;
        cnt[ver]++;
        if(cnt[T]==K) return distance;
        
        for(int i=h[ver];i!=-1;i=ne[i]){
            int j=e[i];
            if(cnt[j]<K)
                heap.push({distance+w[i]+dist[j],{distance+w[i],j}});
        }
    }
    return -1;
}

int main(void)
{
    cin>>n>>m;
    
    memset(h,-1,sizeof(h));
    memset(rh,-1,sizeof(rh));
    
    for(int i=1;i<=m;i++){
        cin>>a>>b>>l;
        add(h,a,b,l);
        add(rh,b,a,l);
    }
    
    cin>>S>>T>>K;
    if(S==T) K++;
    
    dijkstra();
    
    cout<<astar()<<endl;
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值