每周一算法:A*(A Star)算法

这篇具有很好参考价值的文章主要介绍了每周一算法:A*(A Star)算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

八数码难题

题目描述

3 × 3 3\times 3 3×3 的棋盘上,摆有八个棋子,每个棋子上标有 1 1 1 8 8 8 的某一数字。棋盘中留有一个空格,空格用 0 0 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为 123804765 123804765 123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入格式

输入初始状态,一行九个数字,空格用 0 0 0 表示。

输出格式

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数。保证测试数据中无特殊无法到达目标状态数据。

样例 #1

样例输入 #1

283104765

样例输出 #1

4

提示

样例解释

每周一算法:A*(A Star)算法,每周一算法,算法,青少年编程,信息学竞赛,c++

图中标有 0 0 0 的是空格。绿色格子是空格所在位置,橙色格子是下一步可以移动到空格的位置。如图所示,用四步可以达到目标状态。

并且可以证明,不存在更优的策略。

广度优先搜索

算法思想

根据题目描述,输入一个棋盘的初始状态,求从初始状态到目标状态需要的最少移动次数,可以用广度优先搜索求解,基本思想如下:

  • 将初始状态 start \text{start} start的移动步数设为 0 0 0,然后将其加入队列
  • 只要队列不为空
    • 从队首取出一个状态 state \text{state} state
    • 如果 state = end \text{state}=\text{end} state=end结束状态,则搜索结束,返回到达的 state \text{state} state移动步数
    • 否则,找到 state \text{state} state中字符 0 0 0的位置,向相邻方向进行扩展
      • 如果扩展到一个新的状态,则计算扩展到新状态的步数,并将新状态加入队列

代码实现

#include <bits/stdc++.h>
using namespace std;
queue<string> q;
unordered_map<string,int> dis;

int dx[]={-1, 1, 0, 0}, dy[]={0, 0, -1, 1};

int bfs(string start)
{
    string end = "123804765";
    dis[start] = 0;
    q.push(start);
    while(!q.empty())
    {
        string state = q.front(); q.pop();
        int step = dis[state];
        if(state == end) return step;
        int k = state.find('0'); //在当前状态的字符串中找到字符0
        int x = k / 3, y = k % 3; //将字符串中的位置转换为矩阵中的坐标
        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) continue; //越界
            swap(state[k], state[a * 3 + b]); //将数字字符与0进行交换,转移到新状态
            if(!dis.count(state)) //没有访问过
            { 
                dis[state] = step + 1; //转移到state状态的最小步数
                q.push(state); //入队
            }
            swap(state[k], state[a * 3 + b]); //恢复现场,交换回来,为下次转移做准备
        }
    }
    return -1;
}

int main()
{
    string start;
    char c;
    for(int i = 0; i < 9; i ++)
    {
        cin >> c;
        start += c;
    }
    cout << bfs(start) << endl;
    return 0;
}

A*算法

通过BFS可以发现,对每个状态都可以将 0 0 0向上右下左四个方向进行扩展,在最坏情况下要搜索的状态空间为 4 9 4^9 49,指数级别,搜索的效率比较低。在这种情况下,可以使用A*算法进行求解。

A*(A Star)算法是一种很常用的路径查找和图形遍历算法,它有较好的性能和准确度。

A*算法通过下面的函数来计算每个状态的优先级:

f ( n ) = g ( n ) + h ( n ) f(n)=g(n) + h(n) f(n)=g(n)+h(n)
其中:

  • f ( n ) f(n) f(n)是当前状态 n n n综合优先级。当选择下一个要扩展的状态时,我们总会选取综合优先级最高(值最小)的状态。
  • g ( n ) g(n) g(n)是状态距离起点(初始状态)的代价
  • h ( n ) h(n) h(n)是状态 n n n距离终点(目标状态)的预计代价,这也就是A*算法的启发函数

算法思想

A*算法与BFS类似,不同之处在于A*算法使用优先队列,选取 f ( n ) f(n) f(n)值最小(优先级最高)的状态作为下一个待扩展的状态。基本思想如下:

  • 将初始状态 start \text{start} start的移动步数设为 0 0 0,然后其综合优先级初始状态加入优先队列
  • 只要队列不为空
    • 取出优先队列中综合优先级最高(值最小)的状态 state \text{state} state
    • 如果 state = end \text{state}=\text{end} state=end结束状态,则搜索结束,返回到达的 state \text{state} state移动步数
    • 否则,找到 state \text{state} state中字符 0 0 0的位置,向相邻方向进行扩展
      • 如果扩展到一个新状态,或者到达该状态的步数减少,将状态的综合优先级和状态本身继续加入优先队列

启发函数

从算法的基本思想可以看出来,启发函数会影响A*算法的行为。

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

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

对于网格形式的图,有以下这些启发函数可以使用:

  • 如果图形中只允许朝上下左右四个方向移动,则可以使用曼哈顿距离(Manhattan distance)。
  • 如果图形中允许朝八个方向移动,则可以使用对角距离。
  • 如果图形中允许朝任何方向移动,则可以使用欧几里得距离(Euclidean distance)。

代码实现

#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
typedef pair<int, string> PIS;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
//启发函数取当前状态中每个数字与其目标位置的曼哈顿距离之和
int h(string state)
{
    int res = 0;
    for(int i = 0; i < state.size(); i ++)
    {
        if(state[i] != '0')
        {
            int t = state[i] - '1';
            res += abs(i / 3 - t / 3) + abs(i % 3 - t % 3); //累加每个数字到其正确位置的曼哈顿距离
        }
    }
    return res;
}
int astar(string start)
{
    string end = "123804765";
    priority_queue<PIS, vector<PIS>, greater<PIS>> heap; //小顶堆
    unordered_map<string, int> dis; //记录到达每一种状态的步数
    //g(start)返回起点到终点的预估距离
    heap.push({0 + h(start), start});
    dis[start] = 0;
    while(heap.size())
    {
        PIS t = heap.top(); heap.pop();
        string state = t.second;
        if(state == end) break; //终点第一次出队,搜索结束
        int step = dis[state];
        int k = state.find('0'); //找到0所在位置
        int x = k / 3, y = k % 3; 
        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) continue;
            swap(state[x * 3 + y], state[a * 3 + b]); //将0和目标交换
            if(!dis.count(state) || dis[state] > step + 1) //如果扩展到一个新的状态,或者能够缩短到state的距离
            {
                dis[state] = step + 1;
                heap.push({dis[state] + h(state), state}); //将综合优先级和状态加入优先队列
            }
            swap(state[x * 3 + y], state[a * 3 + b]);//恢复现场
        }
    }
    return dis[end];
}
int main()
{
    char c;
    string start;
    for(int i = 0; i < 9; i ++)
    {
        cin >> c;
        start += c;
    }
    cout << astar(start);
    return 0;
}

相关练习

洛谷P2324:骑士精神文章来源地址https://www.toymoban.com/news/detail-840061.html

到了这里,关于每周一算法:A*(A Star)算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [每周一更]-(第82期):认识自然处理语言(NLP)

    GPT的大火,带起了行业内大模型的爆发;国内外都开始拥有或者研发自己的大模型,下边我们从NLP来进一步深入了解大模型、AI。 一、什么是NLP? 自然语言处理 (英语:Natural Language Processing,缩写作 NLP )是人工智能和语言学领域的分支学科。此领域探讨如何处理及运用自然

    2024年01月16日
    浏览(33)
  • [每周一更]-(第54期):Go的多版本管理工具

    参考 https://zhuanlan.zhihu.com/p/611253641 https://learnku.com/articles/78326 前文概要 Go语言从开始使用从1.13起步,随着泛型的支持,带领团队在转型Go的时候,做基础组件架构选型使用1.18,但是Go版本不断迭代想使用最新版本来体验下,类比前端中node,有个nvm工具; 联想到Go应该也会有对

    2024年02月16日
    浏览(29)
  • [每周一更]-(第27期):HTTP压测工具之wrk

    [补充完善往期内容] wrk是一款简单的HTTP压测工具,托管在Github上,https://github.com/wg/wrk wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架. 确切的

    2024年02月03日
    浏览(31)
  • [每周一更]-(第69期):特殊及面试的GIT问题解析

    整合代码使用过程的问题,以及面试遇到的细节,汇总一些常用命令的对比解释和对比; 1、fetch和pull区别 git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。 git pull则是将远程主机的最新内容拉下来后直接合并,即:git pull = git

    2024年02月08日
    浏览(32)
  • 第四届上海市青少年算法竞赛(小学组)

    第四届上海市青少年算法竞赛(小学组) T1 回文串 题目描述 如果一个字符串,顺读与倒读的内容一样,称这个字符串为回文。例如 aka 是一个回文,noon 也是一个回文。 给定一个字符串,请计算最少需要修改多少个字符,才能将这个字符串变成回文。 单次修改可以将字符串

    2024年02月12日
    浏览(36)
  • [每周一更]-(第45期):Docker私有镜像仓库配置并打通阿里云OSS

    Docker Registry 2 官方镜像创建一个私有镜像仓库,将Docker 镜像上传到 OSS 相应的路径中。 参考: BatchCompute Docker支持:https://help.aliyun.com/document_detail/143334.html?spm=a2c4g.143333.0.0.4a6f8752ls18FR Docker Registry:https://docs.docker.com/registry 基于OSS搭建私有 Docker Registry:https://developer.aliyun.com

    2024年02月03日
    浏览(32)
  • [每周一更]-(第83期):Go新项目-Gin中间件的使用和案例(10)

    在 Gin 中,中间件是一种用于处理 HTTP 请求和响应的功能强大的机制。中间件是一段位于请求处理链和最终处理器之间的代码, 它可以截获请求、执行预处理操作,修改请求或响应,然后将控制权传递给下一个中间件或最终的请求处理器。 中间件在业务使用中,方便注入一些

    2024年01月20日
    浏览(41)
  • [每周一更]-(第57期):用Docker、Docker-compose部署一个完整的前后端go+vue分离项目

    将公司物理机项目改为容器化部署,最终方案是通过容器docker-compose部署使项目可以ip+端口访问,再通过物理机nginx代理进行https域名访问。 可能还有更好的方式,开一个nginx的容器,进行代理,但由于跟物理机80,443端口冲突,暂时没有采用。 可能目前处理不是最好的方式,不

    2024年02月14日
    浏览(31)
  • [青少年CTF]-MISC WP(二)

    16)17insanity FLAG:INSA{Youre_crazy_I_like_it} 17)17sanity FLAG:INSA{Youre_sane_Good_for_you} 18)原sher FLAG:qsnctf{c1f5e391-83dd-47e3-9f15-0e32eaafdc95} 19)签到 20)八卦迷宫 FlAG:cazy{zhanchangyangchangzhanyanghechangshanshananzhanyiyizhanyianyichanganyang} 21)我看他是喜欢套娃! 摩斯电码在线转换 培根密码在线加解

    2024年02月14日
    浏览(38)
  • 青少年CTF平台练习密码学

    题目 凯撒大帝在出征之路上留下了这样一串字符,你能通过这串字符得到FLAG并提交吗? 我的解答: 凯撒密码 qsnctf{1c2fee7b8fcdaf7d1e2320acd6a97a9f} 题目 这是什么密码呢?得到的结果请加上qsnctf{}后提交。 我的解答: 猪圈密码:http://moersima.00cha.net/zhuquan.asp 在线解码即可。 题目

    2024年03月09日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包