神奇搜索算法A*
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*的更多相关文章
- 布谷鸟搜索算法CS
0 引言 布谷鸟搜索(Cuckoo Search,CS)是由 Xin-She Yang 和 Suash Deb 于 2009 年开发的自然启发式算法.CS 基于布谷鸟的寄生性育雏(brood para ...
- 焦大:seo该研究用户需求还是搜索算法
http://www.wocaoseo.com/thread-62-1-1.html 上一篇博客我写了用户需求点是做seo排名最首要关注的东西,其实这个我在以前也一直说的,seo有两大核心,一个是检索 ...
- BZOJ 1006 【HNOI2008】 神奇的国度
题目链接:神奇的国度 一篇论文题--神奇的弦图,神奇的MCS-- 感觉我没有什么需要多说的,这里简单介绍一下MCS: 我们给每个点记录一个权值,从后往前依次确定完美消除序列中的点,每次选择权值最大的一 ...
- 前端精选文摘:BFC 神奇背后的原理
BFC 已经是一个耳听熟闻的词语了,网上有许多关于 BFC 的文章,介绍了如何触发 BFC 以及 BFC 的一些用处(如清浮动,防止 margin 重叠等).虽然我知道如何利用 BFC 解决这些问题, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- 一行神奇的javascript代码
写本篇文章的缘由是之前群里@墨尘发了一段js代码,如下: (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~ ...
- [ACM训练] 算法初级 之 搜索算法 之 广度优先算法BFS (POJ 3278+1426+3126+3087+3414)
BFS算法与树的层次遍历很像,具有明显的层次性,一般都是使用队列来实现的!!! 常用步骤: 1.设置访问标记int visited[N],要覆盖所有的可能访问数据个数,这里设置成int而不是bool, ...
- [翻译svg教程]Path元素 svg中最神奇的元素!
先看一个实例 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999 ...
- php isset( $test ) 的神奇之处。
很久一段时间没更新博客了,由于近段时间一直在忙 挑战杯 的项目,所以没怎样把一些总结放上来.这次,总结下 php 的一个 函数 : boolean isset($test), 返回值:boolean类 ...
随机推荐
- 【C/C++开发】C语言实现函数可变参数
函数原型: int printf(const char *format[,argument]...) 返 回 值: 成功则返回实际输出的字符数,失败返回-1. 函数说明: ...
- [C语言]小知识点 持续更新
2019-11-24 1.如果输入: printf(,)); 会得到0: 这和我们的日常判断不相符! 然而,改成: printf(,)); 就可以成功输出“2”: 因此,注意pow函数返回的是浮点数, ...
- 015 Android md5密码加密及其工具类
1.md5加密介绍 MD5算法是广泛使用的杂凑函数,也就是哈希函数,英文全拼是:Message Digest Algorithm,对应的中文名字是消息摘要算法. MD5加密:将字符串转换成 32位的字 ...
- 修改织梦DedeCMS投票漏洞
织梦/dedecms系统我们都知道是有很多漏洞的,我在调试投票功能的时候正好要用到投票功能,这不就出现了漏洞,下面我就给大家展示如何修复这个织梦投票漏洞 首先我们打开//dedevote.class. ...
- 开始Jupyter Notebooks
开始Jupyter Notebooks 安装Anaconda 因为不能有空格,所以没有选C:\Program Files 认识Jupyter Notebooks 修改 jupyter notebook ...
- Linux安装JDK,Tomcat,Mysql+部署项目
安装VMWare虚拟机 下载地址(http://www.onlinedown.net/soft/2062.htm) 安装步骤很简单(除了选择安装路径),傻瓜式安装 同意协议 选择安装路径 安装 完成 ...
- linux 磁盘占用的排查流程
Linux 服务器在使用过程中可能会遇到各种问题,其中之一就是"没有可用空间". 遇到这种情况,就需要进行排查,定位到消耗了磁盘的那个文件夹. 流程如下: 1. df -h df ...
- 编译 Linux 内核 时出现 Restart config 问题
scripts/kconfig/conf --silentoldconfig Kconfig * * Restart config... * * * Enable the block layer * ...
- linux主机内存告警shell脚本
#!/bin/sh ramusage=$(free | awk '/Mem/{printf("RAM Usage: %.2f\n"), $3/$2*100}'| awk '{pri ...
- hdu1171 灵活的运用背包问题咯。。。 还有!!!! 合理的计算数组的范围!! wa了好多次!
Problem Description Nowadays, we all know that Computer College is the biggest department in HDU. Bu ...