AcWing 178:第K短路 ← A* 算法

news/2024/7/24 12:53:27 标签: AStar算法

【题目来源】
https://www.acwing.com/problem/content/description/180/

【题目描述】
给定一张 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≤10^4,
1≤K≤1000 ,
1≤L≤100

【输入样例】
2 2
1 2 5
2 1 4
1 2 2

【输出样例】
14

【算法分析】
一、A* 算法简介

A* algorithm is a popular choice for graph searchBreadth First Search is the simplest of the graph search algorithms. Graph search algorithms, including A*, take a “graph” as input. 
A* algorithm is a modification of Dijkstra’s Algorithm that is optimized for a single destination.
A* algorithm uses both the actual distance from the start and the estimated distance to the goal.
From: https://www.redblobgames.com/pathfinding/a-star/introduction.html

二、A* 算法的估值更新
A* 算法的估值更新,需要用到 A* 算法的
启发函数 h(x)
相应地,A* 算法的
可表示为:f(x)=g(x)+h(x),其中:
f(x):从初始状态经由当前状态 x 到目标状态的估计代价
g(x):从初始状态到当前状态 x 的
实际代价
h(x):从当前状态 x 到目标状态的估计代价

注意:
1. 针对 f(x)=g(x)+h(x),如果 h(x)=0,就是普通的BFS算法;如果 g(x)=0,就是贪心算法。所以
A* 算法就是“BFS+贪心”。
2. 启发函数 h(x) 决定了A*算法的优劣。启发函数 h(x) 包括
曼哈顿距离对角线距离(切比雪夫距离)、欧几里得距离等。启发函数 h(x) 必须满足单调性(即估计代价不会超过从当前状态 x 到目标状态的的实际代价)。A*算法的主要难点设计合适的 h(x) 函数,而编码很容易。
3. 在二维平面上,有3种方法可以近似计算 h(cur)。
下面的 (cur.x, cur.y) 是 cur 点的坐标,(t.x, t.y) 是终点 t 的坐标。
(1)曼哈顿距离
应用场景:只能在四个方向(上,下,左,右)移动。

h(cur)=abs(cur.x–t.x)+abs(cur.y–t.y)
(2)对角线距离
应用场景:可以在八个方向上移动,例如国际象棋的国王的移动。

h(cur)=max(abs(cur.x–t.x), abs(cur.y–t.y))
(3)欧几里得距离。
应用场景:可以向任何方向移动。

h(cur)=sqrt((cur.x–t.x)^2+(cur.y–t.y)^2)
非平面问题,需要设计合适的 h 函数。
4. 计算 h(x) 时,要
忽略路径中的障碍物。因为 h(x) 是对剩余距离的估算值,而不是实际值,因此  h(x) 为估计代价。
5. A*算法中,地图必须是可行的,即不存在无法通过的障碍物或无法到达的区域。同时,需将地图“
网格化”,“网格化”其实就是将连续的问题离散化
6. A*算法中,选取下一个格子的
标准不是离终点最近而是离起点和终点的距离之和最近

三、A* 算法的步骤
在 A* 算法中,需要使用两个辅助表来记录结点。
一个用于记录可被访问的结点,称为
openList 表;一个是记录已访问过的结点,称为 closeList 表。
A* 算法的终止条件是终点在 closeList 表中(找到路径)或 openList 表为空(找不到路径)。
-------------------------------------

A* 算法的简单步骤如下:
1.把起始节点添加到openList;
2.重复如下步骤:
   a) 寻找 openList 当中 F 值最低的结点,将其称为当前结点;
   b) 将该结点从 openList 中删除,并加入 closeList 当中;
   c) 对于当前结点相邻的 8 个结点
       * 如果它不可通过或者已在 closeList 当中,忽略它,反之如下;
       * 如果它不在 openList 当中,将其添加进去。把当前结点作为它的父结点。同时更新它的 F,G 和 H 值。
       * 如果它已经在 openList 当中,通过判断沿当前结点到它的路径的
G 值是否更小,若更小,则将当前结点作为它的父结点,同时更新它的 F 和 G 值。否则,不做任何操作。
   d) 当目标结点已添加到 closeList 时,路径已被找到;或者没有找到目标结点,此时 openList 已为空。这时候,路径不存在。以上两种情况结束循环。
3.
路径回归。从目标结点开始,沿着每一个结点的父结点移动至起始结点,形成的路径即为所求路径。

【算法代码】
代码来源于:https://www.acwing.com/solution/content/21233/

#include <bits/stdc++.h>
using namespace std;

typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
const int N=1005,M=2e5+5;
int e[M],ne[M],h[N],val[M],idx,rh[N];
int dist[N],cnt[N];
bool st[N];
int n,m,S,T,K;

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

void dijkstra() {
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,T});
    memset(dist,0x3f,sizeof dist);
    dist[T]=0;

    while(heap.size()) {
        auto t=heap.top();
        heap.pop();

        int cur=t.second;
        if(st[cur]) continue;
        st[cur]=true;

        for(int i=rh[cur]; i!=-1; i=ne[i]) {
            int j=e[i];
            if(dist[j]>dist[cur]+val[i]) {
                dist[j]=dist[cur]+val[i];
                heap.push({dist[j],j});
            }
        }
    }
}

int astar() {
    priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
    heap.push({dist[S],{0,S}});
    while(heap.size()) {
        auto t=heap.top();
        heap.pop();
        int cur=t.second.second,distance=t.second.first;
        cnt[cur]++;

        if(cnt[T]==K) return distance;

        for(int i=h[cur]; i!=-1; i=ne[i]) {
            int j=e[i];
            if(cnt[j]<K) {
                heap.push({distance+val[i]+dist[j],{distance+val[i],j}});
            }
        }
    }
    return -1;
}

int main() {
    cin>>m>>n;
    memset(h,-1,sizeof h);
    memset(rh,-1,sizeof rh);
    for(int i=0; i<n; i++) {
        int a,b,c;
        cin>>a>>b>>c;
        add(h,a,b,c);
        add(rh,b,a,c);
    }

    cin>>S>>T>>K;
    if(S==T) K++;
    dijkstra();

    cout<<astar();
    return 0;
}

/*
in:
2 2
1 2 5
2 1 4
1 2 2

out:
14
*/




【参考文献】
https://www.acwing.com/solution/content/21233/
https://www.jianshu.com/p/b010a81c2754
https://mp.weixin.qq.com/s/ojRrD4zboF7LVYSwOkWcgQ
https://zhuanlan.zhihu.com/p/595716772
https://zhuanlan.zhihu.com/p/385733813
https://blog.csdn.net/hnjzsyjyj/article/details/126693357
https://cdn.acwing.com/blog/content/38521/
https://www.acwing.com/solution/content/49264/
https://www.redblobgames.com/pathfinding/a-star/implementation.html
https://www.redblobgames.com/pathfinding/a-star/implementation.cpp
https://mp.weixin.qq.com/s/f8SihfGy2VCj5th8IaiLsQ


 


http://www.niftyadmin.cn/n/5261964.html

相关文章

Mo 人工智能教学实训平台年终发布会——发现意外 创造可能

发布会视频回放 –发现意外 创造可能– 在技术迅猛发展的时代里&#xff0c;人工智能教育成为推动社会进步的关键力量&#xff0c;大模型更是各行业的必备技能。为了深度探索教育与人工智能的融合&#xff0c;Mo 人工智能教学实训平台于12月12日举行线上年终发布会&#xff0…

机器学习---朴素贝叶斯算法

朴素贝叶斯&#xff08;Naive Bayes ,NB&#xff09;算法是基于贝叶斯定理与特征条件独立假设的分类方法&#xff0c;该算法是有监督的学习算法&#xff0c;解决的是分类问题&#xff0c;是将一个未知样本分到几个预先已知类别的过程。 朴素贝叶斯的思想就是根据某些个先验概率…

ROS2 学习08 导航Nav2:简介、安装、测试效果、错误处理

1、简介 在ROS2中自动导航使用Nav2来实现。 Nav2 使用几个独立的模块化服务&#xff0c;通过 ROS 2接口&#xff08;例如动作服务器或服务&#xff09;与行为树 (BT) 通信。 Nav2 输入包括&#xff1a;TF转换、一个地图源、一个行为树 (BT) XML 文件和相关的传感器数据源; Nav…

嵌入式软件的模拟量数字化处理

引言 在嵌入式系统中&#xff0c;模拟量通过采样调理电路&#xff0c;转换为电压信号&#xff0c;送入MCU的AD采样口&#xff0c;由MCU的A/D转换单元&#xff0c;实现模数转换&#xff0c;MCU通过PWM&#xff08;或其他的DA方式&#xff09;实现了数模转换。此外MCU对外存在对…

伦敦金收盘线应用有技巧

伦敦金的收盘线的绘制方式相当简单&#xff0c;其实也就是MT4中折线图的画法&#xff0c;我们可以在走势图以金价为纵坐标&#xff0c;而以时间为横坐标&#xff0c;然后将每一日的收盘价位值沿着横坐标一一的连接起来&#xff0c;就形成了伦敦金价的收盘线图。 利用线条简单的…

laravel5.5 里面如果想要使用自定义的数据库连接器

由于项目里面使用到了doris&#xff0c;虽然doris支持mysql协议&#xff0c;但是如果直接把他当mysql使用是行不通的&#xff0c;因为doris并不支持mysql的一些option和mode设置&#xff0c;然后就会一直报错&#xff1a; SQLSTATE[HY000]: General error: 2013 Lost connecti…

数据库中对时间的操作(mySql、Oracle、pgSql)

目录 mySql PGSQL oracle 两个日期年数差 月数差 天数差 小时差 加一年 加一月 加一天 加一小时 加一分钟 加一秒 mySql -- %Y-%m-%d %H:%i:%s 区分大小写 m d i s小写 -- 两个日期年数差 SELECT TIMESTAMPDIFF(YEAR, STR_TO_DATE(2000-12-12,%Y-%m-%d), STR…

java基础知识②:多线程编程、IO流和网络编程、泛型、集合框架

目录 一、多线程编程 二、IO流 三、网络编程 四、泛型 五、集合框架 具体如下&#x1f440;&#xff1a; 一、多线程编程 1、什么是线程&#xff1f;什么是进程&#xff1f;区别又是什么&#xff1f; &#x1f44c;在Java中&#xff0c;进程是操作系统分配资源的最小单位&…