LogoCSP Wiki By Yundou
1 Search

A*搜索算法

定义

A * 搜索算法(英文:A*search algorithm,A * 读作 A-star),简称 A * 算法,是一种在图形平面上,对于有多个节点的路径求出最低通过成本的算法。它属于图遍历(英文:Graph traversal)和最佳优先搜索算法(英文:Best-first search),亦是 BFS 的改进。

过程

定义起点 ss,终点 tt,从起点(初始状态)开始的距离函数 g(x)g(x),到终点(最终状态)的距离函数 h(x)h(x)h(x)h^{\ast}(x)[^note1],以及每个点的估价函数 f(x)=g(x)+h(x)f(x)=g(x)+h(x)

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

如果 hhh\leq h*,则 A * 算法能找到最优解。

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

h=0h=0 时,A * 算法变为 Dijkstra;当 h=0h=0 并且边权为 11 时变为 BFS

例题

八数码

题目大意:在 3×33\times 3 的棋盘上,摆有八个棋子,每个棋子上标有 1188 的某一数字。棋盘中留有一个空格,空格用 00 来表示。空格周围的棋子可以移到空格中,这样原来的位置就会变成空格。给出一种初始布局和目标布局(为了使题目简单,设目标状态如下),找到一种从初始布局到目标布局最少步骤的移动方法。

    123
    804
    765

思路

hh 函数可以定义为,不在应该在的位置的棋子个数。

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

示例代码

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
using namespace std;
constexpr 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] && a.a[i][j] != 0) 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() {
  cin.tie(nullptr)->sync_with_stdio(false);
  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++) {
      cin >> ch;
      f.a[i][j] = ch - '0';
    }
  s.insert(f);
  q.push({f, 0});
  while (!q.empty()) {
    x = q.top();
    q.pop();
    if (!h(x.a)) {  // 判断是否与标准矩阵一致
      cout << x.t << '\n';
      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;  // 查找空格子(0号点)的位置
    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;
}

例题:

Status
Problem
Tags

On this page