A*启发式搜索 其实是两种搜索方法的合成( A*搜索算法 + 启发式搜索),但要真正理解A*搜索算法,还是得先从启发式搜索算法谈起。

何为启发式搜索

启发式搜索算法有点像广度优先搜索,不同的是,它会优先顺着有启发性和具有特定信息的节点搜索下去,这些节点可能是到达目标的最好路径。我们称这个过程为最优(best-first)或启发式搜索。

简单来说,启发式搜索就是对取和不取都做分析,从中选取更优解(或删去无效解)

由于概念过于抽象,我们使用例题讲解。

例题【NOIP2005 普及组】 采药

题目大意:有$ N $ 种物品和一个容量为 \(W\)的背包,每种物品有重量\(wi\)和价值\(ui\) 两种属性,要求选若干个物品(每种物品只能选一次)放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。

很明显这是一个01背包问题,很容易让我们想到使用动态规划和贪心算法。

但在启发式算法的思路是:

我们写一个估价函数 f,可以剪掉所有无效的 0 枝条(就是剪去大量无用不选枝条)。

估价函数 f 的运行过程如下:

我们在取的时候判断一下是不是超过了规定体积(可行性剪枝)。

在不取的时候判断一下不取这个时,剩下的药所有的价值 + 现有的价值是否大于目前找到的最优解(最优性剪枝)。

例题代码

#include <algorithm>
#include <cctype>//isdigit
#include <cstdio>
int n, tot, ans;
struct node
{
    int c, v;//体积与价值
    double cost;
};
using namespace std;
const int N = 10000;
node a[N + 5];
int read()/*快读大法吼啊!比cin scanf都快*/
{
    int x = 0; short w = 0; char ch = 0;
    while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
    while (isdigit(ch)) { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); }
    return w ? -x : x;
}
//所有的启发式搜索都会有一个估价函数。下面是这一题的估价函数。
inline int f(int t, int v) {
    int tot = 0;
    for (int i = 1; t + i <= n; i++)
    {
        if (v >= a[t + i].c)
        {
            v -= a[t + i].c;
            tot += a[t + i].v;
        }
        else
            return (int)(tot + v * a[t + i].cost);
    }
    return tot;
}
bool operator<(const node &a, const node &b){
    return a.cost > b.cost;
} /*等价于
bool cmp(node a,node b){
    return a.cost>b.cost;
}
*/
void DFS(int now, int cv, int cp){
    ans = max(ans, cp);
    if (now > n)
    {
        return;
    }
    if (f(now, cv) + cp > ans)
        DFS(now + 1, cv, cp);
    if (cv - a[now].c >= 0)
        DFS(now + 1, cv - a[now].c, cp + a[now].v);
}
int main()
{
    tot = read(), n = read();
    for (int i = 1; i <= n; i++)
    {
        a[i].c = read(), a[i].v = read();
        a[i].cost = 1.0 * a[i].v / a[i].c;
    }
    sort(a + 1, a + 1 + n);//由于我们重载了<,所以就不需要cmp函数
    DFS(0, tot, 0);
    printf("%d", ans);
    return 0;
}

A*搜寻算法

​ A*搜寻算法,俗称A星算法,作为启发式搜索算法中的一种,这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上。该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。

A*算法最为核心的部分,就在于它的一个估值函数的设计上:

\[f(n)=g(n)+h(n)
\]

定义起点\(s\) ,终点 \(t\)。

从起点(初始状态)开始的距离函数 \(g(x)\)。

到终点(最终状态)的距离函数 \(h(x),h*(x)\) 。

定义每个点的估价函数$ f(n)=g(n)+h(n)$ 。

\(A*\)算法每次从 优先队列 中取出一个 最小的,然后更新相邻的状态。

如果 \(h <= h*\),则 A*算法能找到最优解。

上述条件下,如果 满足三角形不等式,则 A*算法不会将重复结点加入队列

其实……$h = 0 $ 时就是 DFS 算法, 并且边权为 时就是 BFS

例题 八数码

题目大意:在 $ 3$ x \(3\)的棋盘上,摆有八个棋子,每个棋子上标有 1 至 8 的某一数字。棋盘中留有一个空格,空格用 0 来表示。空格周围的棋子可以移到空格中,这样原来的位置就会变成空格。给出一种初始布局和目标布局(为了使题目简单,设目标状态为

123
804
765

)

,找到一种从初始布局到目标布局最少步骤的移动方法。

$h $函数可以定义为,不在应该在的位置的数字个数。

容易发现\(h\) 满足以上两个性质,此题可以使用 A*算法求解。

代码实现:

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <set>
using namespace std;
const int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
int fx, fy;
char ch;
struct matrix {
  int a[5][5];
  bool operator<(matrix x) const {
    for (int i = 1; i <= 3; i++)
      for (int j = 1; j <= 3; j++)
        if (a[i][j] != x.a[i][j]) return a[i][j] < x.a[i][j];
    return false;
  }
} f, st;
int h(matrix a) {
  int ret = 0;
  for (int i = 1; i <= 3; i++)
    for (int j = 1; j <= 3; j++)
      if (a.a[i][j] != st.a[i][j]) ret++;
  return ret;
}
struct node {
  matrix a;
  int t;
  bool operator<(node x) const { return t + h(a) > x.t + h(x.a); }
} x;
priority_queue<node> q;
set<matrix> s;
int main() {
  st.a[1][1] = 1;
  st.a[1][2] = 2;
  st.a[1][3] = 3;
  st.a[2][1] = 8;
  st.a[2][2] = 0;
  st.a[2][3] = 4;
  st.a[3][1] = 7;
  st.a[3][2] = 6;
  st.a[3][3] = 5;
  for (int i = 1; i <= 3; i++)
    for (int j = 1; j <= 3; j++) {
      scanf(" %c", &ch);
      f.a[i][j] = ch - '0';
    }
  q.push({f, 0});
  while (!q.empty()) {
    x = q.top();
    q.pop();
    if (!h(x.a)) {
      printf("%d\n", x.t);
      return 0;
    }
    for (int i = 1; i <= 3; i++)
      for (int j = 1; j <= 3; j++)
        if (!x.a.a[i][j]) fx = i, fy = j;
    for (int i = 0; i < 4; i++) {
      int xx = fx + dx[i], yy = fy + dy[i];
      if (1 <= xx && xx <= 3 && 1 <= yy && yy <= 3) {
        swap(x.a.a[fx][fy], x.a.a[xx][yy]);
        if (!s.count(x.a)) s.insert(x.a), q.push({x.a, x.t + 1});
        swap(x.a.a[fx][fy], x.a.a[xx][yy]);
      }
    }
  }
  return 0;
}

另外例题 K短路 同样可以用A*算法可以思考下如何AC

参考文献

A*启发式搜索的更多相关文章

  1. 启发式搜索A*算法

    A* 寻路算法 (2011-02-15 10:53:11) 转载▼ 标签: 游戏 分类: 算法 概述 虽然掌握了 A* 算法的人认为它容易,但是对于初学者来说, A* 算法还是很复杂的. 搜索区域(T ...

  2. [USACO2002][poj1945]Power Hungry Cows(启发式搜索)

    Power Hungry CowsTime Limit: 1000MS Memory Limit: 30000K Total Submissions: 4570 Accepted: 1120 Desc ...

  3. 【BZOJ】1085: [SCOI2005]骑士精神(A*启发式搜索)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1085 囧啊囧,看了题解后写了个程序,但是样例总过不了T+T,调试了不下于1个小时,肉眼对拍看了根本看 ...

  4. 迭代启发式搜索 IDA*

    本章聚集了一些做了的迭代启发式搜索的题目 为什么只打了迭代启发式搜索? 因为它很好打,有些类似迭代的时候加的最优化剪枝 [因为这个最优化剪枝其实就是你算的估价函数了...] BZOJ 1085 骑士精 ...

  5. Sicily1153-马的周游问题:启发式搜索

    代码地址: https://github.com/laiy/Datastructure-Algorithm/blob/master/sicily/1153.c 题目如下: 1153. 马的周游问题 C ...

  6. Luogu1074靶形数独【启发式搜索】

    Luogu1074靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, ...

  7. [bzoj1805][SCOI2005]骑士精神 [启发式搜索]

    Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2, ...

  8. js版九宫格拼图与启发式搜索(A*算法)

    九宫格拼图游戏大家都很熟悉,这里给大家如介绍何应用状态空间搜索的方式求解拼图的最佳路径和一个游戏dome及自动求解方法: 本文分web版游戏的实现和启发式搜索算法两部分: 先看dome,直接鼠标点击要 ...

  9. 启发式搜索A-Star算法 【寻找 最短路径 算法】【地理几何位置 可利用的情况】

    在处理最短路径问题时,有一种启发式算法是我们应该了解的,由于其有着优秀的探索效率在各自现实项目中多有应用,它就是 A-star 算法,或  A*  算法. 个人观点: A*  算法并不保证找到的路径一 ...

  10. 启发式搜索技术A*

    开篇 这篇文章介绍找最短路径的一种算法,它的字我比较喜欢:启发式搜索. 对于入门的好文章不多,而这篇文章就是为初学者而写的,很适合入门的一篇.文章定位:非专业性A*文章,很适合入门. 有图有真相,先给 ...

随机推荐

  1. Hadoop学习笔记(1)-Hadoop在Ubuntu的安装和使用

    由于小编在本学期有一门课程需要学习hadoop,需要在ubuntu的linux系统下搭建Hadoop环境,在这个过程中遇到一些问题,写下这篇博客来记录这个过程,并把分享给大家. Hadoop的安装方式 ...

  2. 经典算法之归并排序——python和JS实现

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:韩忠康 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...

  3. Java 14 来势汹汹,这回让空指针无处遁形!!

    上篇:Java 14 之模式匹配,非常赞的一个新特性! 相信在坐的每一位 Java 程序员都遇到过空指针异常: NullPointerException(NPE),不甚其烦. 栈长之前也分享几篇避免空 ...

  4. Acid靶机渗透

    Acid渗透靶机实战 攻击机:kali 192.168.41.147 靶机: acid 192.168.41.149 信息收集 ip发现 开启Acid靶机,通过nmap进行局域网存火主机扫描.![]( ...

  5. php 推荐密码加密的方法

    password_hash() 函数 password_hash() 函数用于创建密码的散列(hash) PASSWORD_DEFAULT - 使用 bcrypt 算法 (PHP 5.5.0 默认). ...

  6. CentOS上安装配置 mongodb

    CentOS 首先yum list mongo* 查看是否有关于mongo的安装包,检查后安装即可   mongo 分client端和server端,server启动db服务,client可以连接到s ...

  7. 利用requests, beautifulsoup包爬取股票信息网站

    这是第一次用requests, beautifulsoup实现爬虫,此次爬取的是一个股票信息网站:http://www.gupiaozhishi.net.cn. 实现非常简单,只是为了demo使用的数 ...

  8. DM 源码阅读系列文章(六)relay log 的实现

    2019独角兽企业重金招聘Python工程师标准>>> 作者:张学程 本文为 DM 源码阅读系列文章的第六篇,在 上篇文章 中我们介绍了 binlog replication 处理单 ...

  9. python练习---博客登录(装饰器)

    程序要求: 1),启动程序,首页面应该显示成如下格式: 欢迎来到博客园首页 1:请登录 2:请注册 3:文章页面 4:日记页面 5:评论页面 6:收藏页面 7:注销 8:退出程序2),用户输入选项,3 ...

  10. Docker PHP7官方镜像安装Redies扩展

    2019独角兽企业重金招聘Python工程师标准>>> 直接RUN docker-php-ext-install redis 失败,google得到: ENV PHPREDIS_VE ...