【A*】第K短路

给定一张N个点(编号1,2…N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边。

注意: 每条最短路中至少要包含一条边。

输入格式

第一行包含两个整数N和M。

接下来M行,每行包含三个整数A,B和L,表示点A与点B之间存在有向边,且边长为L。

最后一行包含三个整数S,T和K,分别表示起点S,终点T和第K短路。

输出格式

输出占一行,包含一个整数,表示第K短路的长度,如果第K短路不存在,则输出“-1”。

数据范围

1≤S,T≤N≤1000
0≤M≤1e5
1≤K≤1000
1≤L≤100

输入样例:

2 2
1 2 5
2 1 4
1 2 2

输出样例:

14

思路:A*算法。这里含有一个估算成本,即某个点到终点的估计距离h,若设这个点到起点的距离为d,到终点的实际距离为g,则这里要保证h<=g。以h+d为依据排序更新最短路会比直接更新快许多。 当h==0的时候,这个算法会退化为迪杰斯特拉算法,当h==g时,这个算法会进化成线性算法。

 

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 1e9+7
using namespace std;
const int maxn = 1e3+10,mod=1e9+7;
int n,m,S,T,K;
int dist[maxn],cnt[maxn];
//dist数组用于记录每个点到终点的距离,即A*算法中的估算成本
bool st[maxn];
struct dd{
    int to,w;
    //to为这条边的终点,w为这条边的长度
};
vector<dd>q[maxn],qa[maxn];//存正向图和反向图
struct node{
    int all,diss,id;
    //all为整条线路的长度
    //diss为这个点到起点的距离
    //id为这个点的下标
};
bool operator < (node x,node y){
    return x.all>y.all;
    //优先队列中以整条线路的长度排序
}
void dijkstra(int start){
    //平平无奇的dijkstra算法
    //由于求的是终点到其他点的最短距离,所以用反向图
    memset(dist,inf,sizeof(dist));
    dist[start]=0;
    int u,len;
    while(1){
        u=0;
        for(int i = 1; i <= n; i++)
            if(!st[i])
            if(u==0||dist[u]>dist[i])u=i;
        if(u==0)break;
        st[u]=true;
        len=qa[u].size();
        for(int i = 0; i < len; i++)
            dist[qa[u][i].to]=min(dist[u]+qa[u][i].w,dist[qa[u][i].to]);
    }
}
void astar(){
    priority_queue<node>que;
    if(S==T)K++;//路中至少包含一条边
    que.push({dist[S],0,S});//讲起点放入优先队列中
    node u;dd v;
    int len,distance;
    //广搜过程
    while(!que.empty()){
        u=que.top(),que.pop();
        cnt[u.id]++;//统计每个点经过的次数
        //求得答案则直接输出
        if(u.id==T&&cnt[T]==K){
            printf("%d\n",u.diss);
            return;
        }
        //将u点能到达的点推入优先队列中
        distance=u.diss,len=q[u.id].size();
        for(int i = 0; i < len; i++){
            v=q[u.id][i];
            que.push({distance+v.w+dist[v.to],distance+v.w,v.to});
        }
    }
    //若是不能经过终点K次,则输出-1
    printf("-1\n");
}
int main(){
    scanf("%d %d",&n,&m);
    int x,y,z;
    for(int i = 1; i <= m; i++){
        scanf("%d %d %d",&x,&y,&z);
        q[x].push_back({y,z});//正向图
        qa[y].push_back({x,z});//反向图
    }
    scanf("%d %d %d",&S,&T,&K);
    dijkstra(T);
    astar();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值