A*算法入门

2023-05-16

A ∗ A* A算法的思路可看:

路径规划之 A ∗ A* A 算法

Introduction to the A ∗ A* A Algorithm

提炼一下:

定义起点 s s s,终点 t t t,从起点开始的距离函数 g ( x ) g(x) g(x) ,到终点的距离函数 h 1 ( x ) h_{1}(x) h1(x) h 2 ( x ) h_{2}(x) h2(x),以及每个点的估价函数 f ( x ) = g ( x ) + h 1 ( x ) f(x)=g(x)+h_{1}(x) f(x)=g(x)+h1(x),其中 h 1 ( x ) h_{1}(x) h1(x)是我们定义的点 x x x到终点的预估代价函数, h 2 ( x ) h_{2}(x) h2(x)是点 x x x到终点的实际代价函数

启发函数会影响 A ∗ A* A算法的行为:

  • 在极端情况下,当启发函数 h 1 ( x ) h_{1}(x) h1(x)始终为0,则将由 g ( x ) g(x) g(x)决定节点的优先级,此时算法就退化成了Dijkstra算法
  • 如果 h 1 ( x ) h_{1}(x) h1(x)始终小于等于节点 x x x到终点的代价 h 2 ( x ) h_{2}(x) h2(x),则 A ∗ A* A算法保证一定能够找到最短路径。但是当 h 1 ( x ) h_{1}(x) h1(x)的值越小,算法将遍历越多的节点,也就导致算法越慢
  • 如果 h 1 ( x ) h_{1}(x) h1(x)完全等于节点 x x x到终点的代价 h 2 ( x ) h_{2}(x) h2(x),则 A ∗ A* A算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点,因为在没有达到终点之前,我们很难确切算出距离终点还有多远
  • 如果 h 1 ( x ) h_{1}(x) h1(x)的值比节点 x x x到终点的代价 h 2 ( x ) h_{2}(x) h2(x)要大,则 A ∗ A* A算法不能保证找到最短路径,不过此时会很快
  • 在另外一个极端情况下,如果 h 1 ( x ) h_{1}(x) h1(x)相较于 g ( x ) g(x) g(x)大很多,则此时只有 h 1 ( x ) h_{1}(x) h1(x)产生效果,这也就变成了最佳优先搜索

所以通过调节启发函数,我们可以控制算法的速度和精确度,在一些情况,我们可能未必需要最短路径,而是希望能够尽快找到一个路径即可,这也是 A ∗ A* A算法比较灵活的地方

例一:

八数码

思路:

我们知道,在平面上,坐标 ( x 1 , y 1 ) (x_{1},y_{1}) (x1,y1) i i i点与坐标 ( x 2 , y 2 ) (x_{2},y_{2}) (x2,y2) j j j点的曼哈顿距离为: d ( i , j ) = ∣ x 1 − x 2 ∣ + ∣ y 2 − y 1 ∣ d(i,j)=|x_{1}-x_{2}|+|y_{2}-y_{1}| d(i,j)=x1x2+y2y1,所以,用每个数和其最终位置的曼哈顿距离作为 h 1 ( x ) h_{1}(x) h1(x),因为其小于 h 2 ( x ) h_{2}(x) h2(x)且差别又不大,所以可以很好的优化

代码:

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

using namespace std;

typedef pair<int , string> PIS;

unordered_map<string , int> dist;
unordered_map<string , pair<string , char>> pre;
priority_queue<PIS , vector<PIS> , greater<PIS>> heap;
string ed = "12345678x";
int dx[4] = {-1 , 0 , 1 , 0} , dy[4] = {0 , 1 , 0 , -1};
char op[] = "urdl";

int f(string state){//求估值函数,即曼哈顿距离
    int res = 0;
    for(int i = 0 ; i < 9 ; i++){
        if(state[i] != 'x'){
            int t = state[i] - '1';
            res += abs(t / 3 - i / 3) + abs(t % 3 - i % 3);
        }
    }
    return res;
}

string bfs(string start){
    heap.push({f(start) , start});
    dist[start] = 0;

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

        string state = t.second;
        int step = dist[state];//记录到达state的实际距离
        if(state == ed) break;//如果到达终点就break

        int k = state.find('x');
        int x = k / 3 , y = k % 3;

        string source = state;//因为在下面state会变,所以留一个备份
        for (int i = 0; i < 4; i ++ ){
            int a = x + dx[i], b = y + dy[i];
            if (a >= 0 && a < 3 && b >= 0 && b < 3){
                swap(state[x * 3 + y], state[a * 3 + b]);
                if (!dist.count(state) || dist[state] > step + 1){
                    dist[state] = step + 1;
                    pre[state] = {source, op[i]};
                    heap.push({dist[state] + f(state), state});
                }
                swap(state[x * 3 + y], state[a * 3 + b]);//恢复回来
            }
        }

    }

    string res;
    while(ed != start){
        res += pre[ed].second;
        ed = pre[ed].first;
    }
    reverse(res.begin() , res.end());
    return res;
}

int main(){
    string start , seq;
    for(int i = 0 ; i < 9 ; i++){
        char c;
        cin >> c;
        start += c;
        if(c != 'x') seq += c;
    }
	//判断逆序对的数量,如果为奇数,直接无解
    int cnt = 0;
    for(int i = 0 ; i < 8 ; i ++)
        for(int j = i + 1 ; j < 8 ; j++)
            if(seq[i] > seq[j])
                cnt++;

    if(cnt % 2) puts("unsolvable");
    else cout << bfs(start) << endl;

    return 0;
}

发现可以优化,一个点其实只需进队列一次就可以了:

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

using namespace std;

typedef pair<int , string> PIS;

unordered_map<string , int> dist;
unordered_map<string , bool> st;
unordered_map<string , pair<string , char>> pre;
priority_queue<PIS , vector<PIS> , greater<PIS>> heap;
string ed = "12345678x";
int dx[4] = {-1 , 0 , 1 , 0} , dy[4] = {0 , 1 , 0 , -1};
char op[] = "urdl";

int f(string state){//求估值函数,即曼哈顿距离
    int res = 0;
    for(int i = 0 ; i < 9 ; i++){
        if(state[i] != 'x'){
            int t = state[i] - '1';
            res += abs(t / 3 - i / 3) + abs(t % 3 - i % 3);
        }
    }
    return res;
}

string bfs(string start){
    heap.push({f(start) , start});
    dist[start] = 0;

    while(heap.size()){
        auto t = heap.top();heap.pop();
		
        string state = t.second;
        
        if(st[state])continue;
        st[state]=true;
        
        int step = dist[state];//记录到达state的实际距离
        
        if(state == ed) break;//如果到达终点就break

        int k = state.find('x');
        int x = k / 3 , y = k % 3;

        string source = state;//因为在下面state会变,所以留一个备份
        for (int i = 0; i < 4; i ++ ){
            int a = x + dx[i], b = y + dy[i];
            if (a >= 0 && a < 3 && b >= 0 && b < 3){
                swap(state[x * 3 + y], state[a * 3 + b]);
                if (!dist.count(state) || dist[state] > step + 1){
                    dist[state] = step + 1;
                    pre[state] = {source, op[i]};
                    heap.push({dist[state] + f(state), state});
                }
                swap(state[x * 3 + y], state[a * 3 + b]);//恢复回来
            }
        }

    }

    string res;
    while(ed != start){
        res += pre[ed].second;
        ed = pre[ed].first;
    }
    reverse(res.begin() , res.end());
    return res;
}

int main(){
    string start , seq;
    for(int i = 0 ; i < 9 ; i++){
        char c;
        cin >> c;
        start += c;
        if(c != 'x') seq += c;
    }
	//判断逆序对的数量,如果为奇数,直接无解
    int cnt = 0;
    for(int i = 0 ; i < 8 ; i ++)
        for(int j = i + 1 ; j < 8 ; j++)
            if(seq[i] > seq[j])
                cnt++;

    if(cnt % 2) puts("unsolvable");
    else cout << bfs(start) << endl;

    return 0;
}

例二:

k短路

思路:

首先,小根堆每次出堆且是终点第几次出堆就是第几短路(第 k k k次到达终点时的路径长度即为第 k k k短路的长度),然后设计代价函数 h 1 ( x ) h_{1}(x) h1(x):从 x x x点到终点的最短距离,其求法:建反向边跑dijkstra即可

代码:

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

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;//最小距离,点位置
typedef pair<int, PII> PIII;//预估代价,最小距离,点位置

const int N = 1010, M = 200010;

int n, m, S, T, K;
int h[N], rh[N], e[M], w[M], ne[M], idx;
int dist[N], cnt[N];
bool st[N];

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

void dijkstra(){//跑反图,x的预估代价输出到dist[x]
    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 ver = t.y;
        if (st[ver]) continue;
        st[ver] = true;

        for (int i = rh[ver]; ~i; i = ne[i]){
            int j = e[i];
            if (dist[j] > dist[ver] + w[i]){
                dist[j] = dist[ver] + w[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 ver = t.y.y, distance = t.y.x;
        cnt[ver] ++ ;
        if (cnt[T] == K) return distance;

        for (int i = h[ver]; ~i; 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(){
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    memset(rh, -1, sizeof rh);

    for (int i = 0; i < m; i ++ ){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(h, a, b, c);
        add(rh, b, a, c);
    }
    scanf("%d%d%d", &S, &T, &K);
    if (S == T) K ++ ;

    dijkstra();
    printf("%d\n", astar());

    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

A*算法入门 的相关文章

随机推荐

  • 阿里云轻量应用服务器centos7搭建hadoop伪分布式集群

    centos7上hadoop伪分布式集群配置 写在前面 云计算实验 xff0c hadoop完全式分布集群 xff0c 没那么多服务器就搞了个伪分布式 纯粹为了应付老师 xff0c 教程是一边查一遍弄的 xff0c 重复装了一次 xff0c
  • Eigen/Sparse稀疏矩阵SparseMatrix

    关于Eigen的稀疏矩阵的介绍 xff1a 原文链接 1 SparseMatrix的格式 SparseMatrix主要包含以下4个数组 xff1a Values stores the coefficient values of the no
  • SpringBoot事件监听器的四种方式

    Java事件监听 事件监听的概念 xff1a 事件监听就是让电脑通过你的操作得到一些数据并对这些数据做出反应 xff0c 反馈相应的行为或者指令的操作 java中的事件机制的参与者有3种角色 xff1b event object 事件状态对
  • OAI搭建步骤(EPC+eNB)

    声明 xff1a 本文CSDN作者原创投稿文章 xff0c 未经许可禁止任何形式的转载 xff0c 原文链接 文章目录 一 系统概述二 搭建核心网EPC openair cn 2 1 准备主机2 2 更换内核2 3 获取openair cn
  • Layui网址

    http laizhefa com layer index html
  • Spring Security

    Spring Security简介 Spring Security是一个高度自定义的安全框架 利用Spring IOC DI和AOP功能 xff0c 为系统提供了声明式安全访问控制功能 xff0c 减少了为系统安全而编写大量重复代码的工作
  • Hibernate基本使用

    Hibrenate框架 一 Hibrenate 1 是一个持久层框架 xff0c 实现jdbc的封装 是一个全自动的ORM框架 2 ORM xff1a 对象 关系 数据库表 映射 xff0c orm致力于将数据库操作转换成Java程序熟悉的
  • Nginx学习笔记

    文章目录 一 Nginx初始二 正向代理和反向代理 xff08 一 xff09 正向代理 xff08 二 xff09 正向代理的使用场景 xff08 三 xff09 反向代理 xff08 四 xff09 反向代理的使用场景 三 Nginx的
  • Mybatis+Mybatis-plus+SpringBoot整合(完整版)

    文章目录 一 Mybatis xff08 一 xff09 Mybatis简介1 Mybatis历史2 Mybatis特性3 Mybatis下载4 和其它持久化层技术对比 xff08 二 xff09 搭建Mybatis1 MySQL不同版本的
  • SpringMVC+SSM整合(完整版)

    文章目录 一 SpringMVC xff08 一 xff09 SpringMVC简介1 什么是MVC2 什么是SpringMVC3 SpringMVC的特点4 MVC的工作流程 xff08 二 xff09 入门案例1 创建maven工程 引
  • C语言实现CNN的前向推理

    利用Python训练模型 xff0c 提取模型参数到C语言的头文件 xff0c C语言进行前向推理计算 目录 1 填充算子1 1多维数组实现1 2一维数组实现 2 卷积算子2 1多维数组实现2 2一维数组实现 3 池化算子3 1多维数组实现
  • 树莓派3B+安装系统

    系统镜像下载 树莓派官方镜像下载地址 xff1a https www raspberrypi org downloads xff08 如果找不到就点击See all download options xff09 https www rasp
  • ubuntu16.04安装中文输入法并设置显示中文

    参考自 xff1a https jingyan baidu com article bad08e1ef4b2f109c85121b7 html 原材料 xff1a ubuntu16 步骤 xff1a 1 在桌面的最左边选择设置 xff08
  • 用PrtSc键触发启动flameshot

    进入系统设置中的 键盘快捷键 先将系统默认的快捷键prt sc禁用 xff0c 否则可能不能再设置用这个快捷键 页面中会列出所有现有的键盘快捷键 xff0c 拉到底部会看见一个 43 按钮 点击 43 按钮添加自定义快捷键并输入以下两个字段
  • 在GitHub的README中使图片深浅主题自适应

    声明 xff1a 本文CSDN作者原创投稿文章 xff0c 未经许可禁止任何形式的转载 xff0c 原文链接 在GitHub的Markdown文件中 xff0c 我们可以针对同一图片使用两份深浅主题的链接 xff0c 以保证在适应GitHu
  • P4:正则表达式(Regular Expression)学习笔记

    正则表达式学习 1 初始正则表达式1 1正则表达式练习11 2正则表达式练习21 3正则表达式练习3 2 正则表达式源码分析2 1 源码分析 matcher find 2 2源码分析 matcher group 0 2 3 整体代码2 4源
  • AutoML-sklearn and torch

    一 auto sklearn 1 1 环境依赖 额外安装swig 第三方库 linux 支持 mac xff0c windows不支持 1 2 示例代码 time left for this task 设定任务最大时间 per run ti
  • AB实验人群定向HTE模型5 - Meta Learner

    Meta Learner和之前介绍的Causal Tree直接估计模型不同 xff0c 属于间接估计模型的一种 它并不直接对treatment effect进行建模 xff0c 而是通过对response effect target 进行建
  • 【第一季】全面认识海思HI3518E方案和SDK环境搭建

    文章内容来自朱有鹏的 朱老师物联网大讲堂 的嵌入式企业级项目 海思HI3518E方案视频编解码传输深度学习 xff0c 转载请注明出处 目录 一 xff0c 视频设备开发的技术流二 xff0c HI3518E方案系统整体架构介绍三 xff0
  • A*算法入门

    A A A 算法的思路可看 xff1a 路径规划之 A A