A*
A*(念做:A Star)算法是一种很常用的路径查找和图形遍历算法,相比于bfs有较好的性能和准确度。
定义比较函数
[f(n)=g(n)+h(n)
]
其中(g(n))为节点(n)距离起点的代价,(h(n))为启发函数,表示预估代价,(f(n))则表示节点的优先级。我们每次选择节点时都优先选择(f(n))最小的节点拓展路线
设定两个集合(A,B),分别存贮待遍历的节点与已经遍历过的节点
对于网格图来说,如果只允许上下左右移动,则启发函数(h(x))可以为两点之间的曼哈顿距离
如果八个方向,则可以用对角距离
任意方向,则使用欧几里得距离
IDA*
迭代加深搜索
每次改变搜索深度,直到搜到答案
P1379 八数码难题
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
这题可以双向bfs
A*
IDA*
由于这道题答案范围不会太大,可以考虑IDA*
启发函数h为所有数字离正确位置的曼哈顿距离,每次增加搜索深度,搜到则出解
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
const int d[4]={-1,0,0,1},f[4]={0,-1,1,0},w[9]={2,1,1,1,2,3,3,3,2},z[9]={2,1,2,3,3,3,2,1,1};
int ans=-1,flag,a[4][4];
int guess()
{
int i,j,sum=0;
for (i=1;i<4;i++)
for (j=1;j<4;j++)
if (a[i][j]) sum+=abs(w[a[i][j]]-i)+abs(z[a[i][j]]-j);
return sum;
}
void dfs(int now,int x,int y)
{
if (flag) return;
if (now>ans) return;
int v=guess(),i,m,n;
if (v+now>ans) return;
if (!v) { flag=1; return; }
for (i=0;i<4;i++)
{
m=x+d[i],n=y+f[i];
if (m && m<4 && n && n<4)
{
swap(a[x][y],a[m][n]);
dfs(now+1,m,n);
swap(a[x][y],a[m][n]);
}
}
}
int main()
{
int i,j,x,y;
for (i=1;i<4;i++)
for (j=1;j<4;j++)
{
a[i][j]=getchar()-48;
if (!a[i][j]) x=i,y=j;
}
while (!flag) ans++,dfs(0,x,y);
printf("%d
",ans);
return 0;
}