A*

A*是一种启发式搜索算法,又叫最佳图搜索算法。

何谓启发式搜索?

众所周知,计算机在执行搜索算法时是没开上帝视角的。因此,在搜索时,往往显得盲目,把所有可能的状态全部遍历,这种搜索我们统称盲目搜索。

启发式搜索,顾名思义,其实就是给了盲目的计算机一点启发,一点智慧,利用这些来引导计算机减少搜索范围,大幅降低搜索复杂度。试想在BFS的过程中,在某一阶段某些状态成为正解的概率明显比别的状态要大,那我们就优先选择这些状态继续BFS,达到降低复杂度的目的。那么怎么计算这个概率呢?

我们需要给计算机一些启发信息。不同的启发信息有不同的强度,既不能太强也不能弱,太强会导致得不到正解,而太弱又会导致优化效果不明显。都说了这么多了,那就来点具体的。

如何进行启发式搜索?

在A*之前,首先要提一下A算法,A*是A的一种特殊情形。

这里我们要引入一个函数——“估价函数”。

定义一个函数\(f(n)=g(n)+h(n)\),利用启发信息计算出\(h\),根据\(f\)函数的值找出当前搜索状态下的最有希望的节点进行扩展。其中\(n\)是一个状态,\(f\)就是估价函数,\(g\)是\(n\)已经用掉的开销(可以理解做裸的BFS用到的信息),\(h\)是一个启发函数。

启发函数在不同的情况下是有不同的计算方法的,这要因情况而异。

为了更好定义A*算法,我们还要引入一些函数:\(f^ *(n),g^ *(n),h^ *(n)\)。

首先我们要明确,上面提到的估价函数,是对待扩展节点的一个“估计”,是从启发信息计算而来的,因此从初始状态到正解对应的状态的花费不是实际花费。

\(f^ *(n)=g^ *(n)+h^ *(n)\)表示的是从搜索的起点经过\(n\)状态到达搜索目标的实际最小花费,因此,你可以把\(f(n),g(n),h(n)\)看作这些带*的函数的估计值。

A*算法定义为保证\(h(n)<h^ *(n)\)的A算法,其保证能得到最优解。

我们通过例题入手

P1379 八数码难题

题目描述

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

显然这题可以用双向BFS做(起点状态和中点状态模式相同),而且实际上裸的BFS这题也能过。。。

不过既然是在讲A*,就讨论A*怎么解(哇,发现题解有一篇我校巨佬的IDA*QWQ)。

为了使得\(h\)函数一定比\(h^ *(n)\)小,我们考虑构造一个这样的函数。在起始状态,\(h ^*(n)\)显然就是所有位置的数移动到目标状态的位置的所需步数。我们可以假定\(h(n)\)为\(n\)状态时不在目标位置的数的个数。因为显然,这些不再目标位置的数至少要通过一次移动才能到达目标位置。这是一个可行方案。

贴代码:

用了一个map来去除重复状态,其实matrix结构体里面随便咋重载都行,反正装的进去map就行。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
using namespace std;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
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(x.a[i][j]!=a[i][j]) return x.a[i][j]<a[i][j];
return 0;
}
}st,f;
inline int h(matrix a)
{
int tmp=0;
for(int i=1;i<=3;++i)
for(int j=1;j<=3;++j)
if(a.a[i][j]!=st.a[i][j]) tmp++;
return tmp;
}
struct node{
matrix a;
int step;
bool operator<(node x) const{
return x.step+h(x.a)<step+h(a);
}
node(){};
node(matrix _a,int _step){a=_a,step=_step;}
};
priority_queue<node> q;
map<matrix,bool> mp;
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;
char c;
for(int i=1;i<=3;++i)
for(int j=1;j<=3;++j){
scanf("%c",&c);
f.a[i][j]=c-'0';
}
q.push(node(f,0));
while(q.size()){
node x=q.top();q.pop();
if(!h(x.a)){
printf("%d\n",x.step);
return 0;
}
if(mp[x.a]) continue;
mp[x.a]=1;
int nx,ny;
for(int i=1;i<=3;++i)
for(int j=1;j<=3;++j)
if(!x.a.a[i][j]) nx=i,ny=j;
for(int i=0;i<4;++i){
int xx=nx+dir[i][0],yy=ny+dir[i][1];
if(xx>=1&&xx<=3&&yy>=1&&yy<=3){
swap(x.a.a[xx][yy],x.a.a[nx][ny]);
q.push(node(x.a,x.step+1));
swap(x.a.a[xx][yy],x.a.a[nx][ny]);
}
}
}
return 0;
}

如果要加ID的话会更快,这里不详细讲,具体做法跟一般IDBFS是一样的。


P2324 [SCOI2005]骑士精神

跟上面这道题完全一样,没区别,稍微改一下代码就好了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
int dir[8][2]={{1,2},{-1,2},{2,1},{2,-1},{-2,1},{-2,-1},{1,-2},{-1,-2}};
struct matrix{
int a[6][6];
bool operator<(matrix x) const{
for(int i=1;i<=5;++i)
for(int j=1;j<=5;++j)
if(x.a[i][j]!=a[i][j]) return x.a[i][j]<a[i][j];
return 0;
}
}st,f;
inline int h(matrix a)
{
int tmp=0;
for(int i=1;i<=5;++i)
for(int j=1;j<=5;++j)
if(a.a[i][j]!=st.a[i][j]) tmp++;
return tmp;
}
struct node{
matrix a;
int step;
bool operator<(node x) const{
return x.step+h(x.a)<step+h(a);
}
node(){};
node(matrix _a,int _step){a=_a,step=_step;}
};
priority_queue<node> q;
map<matrix,bool> mp;
int main()
{
st.a[1][1]=1,st.a[1][2]=1,st.a[1][3]=1,st.a[1][4]=1,st.a[1][5]=1;
st.a[2][1]=0,st.a[2][2]=1,st.a[2][3]=1,st.a[2][4]=1,st.a[2][5]=1;
st.a[3][1]=0,st.a[3][2]=0,st.a[3][3]=-1,st.a[3][4]=1,st.a[3][5]=1;
st.a[4][1]=0,st.a[4][2]=0,st.a[4][3]=0,st.a[4][4]=0,st.a[4][5]=1;
st.a[5][1]=0,st.a[5][2]=0,st.a[5][3]=0,st.a[5][4]=0,st.a[5][5]=0;
int T;
scanf("%d",&T);
while(T--){
mp.clear();
while(q.size()) q.pop();
char c;
for(int i=1;i<=5;++i)
for(int j=1;j<=5;++j){
scanf(" %c",&c);
if(c=='*') f.a[i][j]=-1;
else f.a[i][j]=c-'0';
}
bool flag=0;
q.push(node(f,0));
while(q.size()){
node x=q.top();q.pop();
if(flag) continue;
if(x.step>15){
printf("-1\n");
flag=1;
}
if(!h(x.a)&&!flag){
printf("%d\n",x.step);
flag=1;
}
if(mp[x.a]) continue;
mp[x.a]=1;
int nx,ny;
for(int i=1;i<=5;++i)
for(int j=1;j<=5;++j)
if(x.a.a[i][j]==-1) nx=i,ny=j;
for(int i=0;i<8;++i){
int xx=nx+dir[i][0],yy=ny+dir[i][1];
if(xx>=1&&xx<=5&&yy>=1&&yy<=5){
swap(x.a.a[xx][yy],x.a.a[nx][ny]);
q.push(node(x.a,x.step+1));
swap(x.a.a[xx][yy],x.a.a[nx][ny]);
}
}
}
}
return 0;
}

就是跑的比较慢啦。

神奇搜索算法A*的更多相关文章

  1. 布谷鸟搜索算法CS

    0 引言 布谷鸟搜索(Cuckoo Search,CS)是由 Xin-She Yang 和 Suash Deb 于 2009 年开发的自然启发式算法.CS 基于布谷鸟的寄生性育雏(brood para ...

  2. 焦大:seo该研究用户需求还是搜索算法

    http://www.wocaoseo.com/thread-62-1-1.html 上一篇博客我写了用户需求点是做seo排名最首要关注的东西,其实这个我在以前也一直说的,seo有两大核心,一个是检索 ...

  3. BZOJ 1006 【HNOI2008】 神奇的国度

    题目链接:神奇的国度 一篇论文题--神奇的弦图,神奇的MCS-- 感觉我没有什么需要多说的,这里简单介绍一下MCS: 我们给每个点记录一个权值,从后往前依次确定完美消除序列中的点,每次选择权值最大的一 ...

  4. 前端精选文摘:BFC 神奇背后的原理

    BFC 已经是一个耳听熟闻的词语了,网上有许多关于 BFC 的文章,介绍了如何触发 BFC 以及 BFC 的一些用处(如清浮动,防止 margin 重叠等).虽然我知道如何利用 BFC 解决这些问题, ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  6. 一行神奇的javascript代码

    写本篇文章的缘由是之前群里@墨尘发了一段js代码,如下: (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~ ...

  7. [ACM训练] 算法初级 之 搜索算法 之 广度优先算法BFS (POJ 3278+1426+3126+3087+3414)

    BFS算法与树的层次遍历很像,具有明显的层次性,一般都是使用队列来实现的!!! 常用步骤: 1.设置访问标记int visited[N],要覆盖所有的可能访问数据个数,这里设置成int而不是bool, ...

  8. [翻译svg教程]Path元素 svg中最神奇的元素!

    先看一个实例 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999 ...

  9. php isset( $test ) 的神奇之处。

    很久一段时间没更新博客了,由于近段时间一直在忙 挑战杯 的项目,所以没怎样把一些总结放上来.这次,总结下 php 的一个 函数 : boolean isset($test), 返回值:boolean类 ...

随机推荐

  1. Django:ORM中ForeignKey外键关系分析

    假设有两张表,Role和User,因为多个用户会对应一个角色,属于多对一关系,所以User中的rolename字段使用ForeignKey,第一个参数为要关联的表Role,第二个参数related_n ...

  2. AWS 消息服务(九)

    松耦合架构 概述 使用独立的组件设计架构,降低相互依赖,当一个组件出现故障时,其他不受影响 利用ELB和SQS来打破传统服务器各层的关联,成为各层之间的中介,各层的故障和扩展均由中介自助处理 系统的耦 ...

  3. LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)

    199. 二叉树的右视图 199. Binary Tree Right Side View 题目描述 给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值. Giv ...

  4. Pygame01之游戏开发

    一.Pygame库 Pygame是一个利用SDL库写的游戏库,SDL库全名:Simple DirectMedia Layer,据说是SamLantinga写的大牛写的为了让Loki(公司)更好的向li ...

  5. go select 的default

    当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行. 为了非阻塞的发送或者接收,可使用 default 分支: select { case i := <-c: ...

  6. fabric.js 知识点整理

    fabric.js是一个很好用的 canvas 操作插件,下面整理了一些平时项目中用到的知识点: //1: 获得画布上的所有对象: var items = canvas.getObjects(); / ...

  7. jvm--工具

    jps (java process status) == ps / top 作用:显示所有运行中的java进程. jstat 作用:查看类装载,内存,垃圾收集,jit编译的信息. jinfo 作用:实 ...

  8. SaltStack实现动态文件分发,支持脚本换行,中文乱码

    场景:将动态脚本分发到各个机器的指定目录下 说明:使用SaltStack的 file.managed file.managed 里面可以定义内容的几种方式 - source: - contents: ...

  9. 在论坛中出现的比较难的sql问题:38(字符拆分 字符串检索问题)

    原文:在论坛中出现的比较难的sql问题:38(字符拆分 字符串检索问题) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉得 ...

  10. k8s 开源web操作平台

    https://kuboard.cn/install/install-dashboard.html kuborad