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. Eugene and an array CodeForces - 1333C (思维)

    题目大意:求好数组的个数,所谓好数组 1好数组是原数组的一段连续的子数组,2 好数组不包含元素和为0的子数组. 题解:唉,这个题目把我给些懵了....我一开始的想法求后缀和,保存位置,然后枚举前缀和, ...

  2. Treasure Island DFS +存图

    All of us love treasures, right? That's why young Vasya is heading for a Treasure Island. Treasure I ...

  3. Os-hackNos-1靶机过关记录

    靶机地址:172.16.1.198(或112)  kali地址:172.16.1.108 1 信息收集 靶机界面如下 简单查看 OS:Ubuntu Web:Apache2.4.18 尝试端口扫描 开放 ...

  4. SpringMVC视图解析中的 forward: 与 redirect: 前缀

    在 SpringMVC 中,可以指定画面的跳转方式.使用 forward: 前缀实现请求转发跳转,使用 redirect: 前缀实现重定向跳转.有前缀的转发和重定向操作和配置的视图解析器没有关系,视图 ...

  5. Java 解析 xml 常见的4中方式:DOM SAX JDOM DOM4J

    Java 四种解析 XML 的特点 1.DOM 解析: 形成了树结构,有助于更好的理解.掌握,且代码容易编写. 解析过程中,树结构保存在内存中,方便修改. 2.SAX 解析: 采用事件驱动模式,对内存 ...

  6. Inno Setup 添加版权信息

    [Setup]AppCopyright=Copyright (C) - My Company, Inc. 有以上一句,即可在右键 --> Property --> Details 里看见版 ...

  7. Session服务器之Redis

    Session服务器之Redis Redis与Memcached的区别内存利用率:使用简单的key value (键值对)存储的话,Mermcached 的内存利用率更高,而如果Redis采用hash ...

  8. CUBA:如何准备上线

            "在我电脑上是好的呢!"现在看来,这句话更像是调侃开发人员的一个段子,但是"开发环境与生产环境"之间的矛盾依然存在.作为开发者,你需要记住,你写 ...

  9. jQuery动态时钟

    效果图: 引用的jQuery.js自己百度 代码: <!DOCTYPE html> <html> <head> <meta charset="utf ...

  10. 1309:【例1.6】回文数(Noip1999)

    传送门:http://ybt.ssoier.cn:8088/problem_show.php?pid=1309 [题目描述] 若一个数(首位不为零)从左向右读与从右向左读都是一样,我们就将其称之为回文 ...