昨天啃了一阵子,终于啃下了大名鼎鼎的A*(AStar)算法。
A*跟BFS(宽度优先搜索)非常相似,只是多了一个启发函数跟结点的实时判断。
而A*算法的重点也就是启发函数,而且这没有固定的模版或者套路,完全看个人的设计能力。
其他的并不难。
参考了 http://hi.baidu.com/catro/item/4782da1769edbd721109b5e9
和 http://blueve.me/archives/684
这两篇写得非常好的文章。
废话不说了,稚嫩的代码如下:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
//A*八数码
// 状态结构存储
struct State
{
int s[3][3];
int index,pre;
int g;
//打印路径
void Display()
{
for(int i = 0; i < 3; ++i)
{
for(int j = 0; j < 3; ++j)
{
cout << s[i][j] << ' ';
}
cout << endl;
}
}
//优先级比较
friend bool operator< (State n1, State n2)
{
return n1.g > n2.g;
}
bool equal(State goal)
{
for(int i = 0; i < 3; ++i)
{
for(int j = 0; j < 3; ++j)
{
if(s[i][j] != goal.s[i][j]) return false;
}
}
return true;
}
int fineZero(int flag)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(s[i][j]==0)
{
if(flag==0) return i;
else return j;
}
}
};
//vector<State> States; // 存储所有的状态
//初始状态和目标状态
State inital,goal;
//开闭表
priority_queue<State> openTable;
vector<State> closeTable;
int direct[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; //方向 右 左 下 上
/**
* 启发函数
* --------------------
* f(s) = g(s) + h(s)
* 其中 g(s) 由 g 数组给出
* h(s) = 与目标状态不一致点的个数
*/
int f(State node)
{
int rslt = node.g; // g(s)
for(int i = 0; i < 3; ++i) // h(s)
for(int j = 0; j < 3; ++j)
{
if(node.s[i][j] != goal.s[i][j])
{
rslt++;
}
}
return rslt;
}
bool isMove(int goalX , int goalY)
{
if(goalX < 0 || goalX>=3) return false;
if(goalY < 0 || goalY>=3) return false;
return true;
}
bool isCloseTableContain(State node)
{
State tmp;
for(vector<State>::iterator ite = closeTable.begin() ; ite != closeTable.end() ;ite++)
{
tmp = (State)*ite;
if(tmp.equal(node))
return true;
}
return false;
}
State AStar(State start)
{
// 初始状态
start.g = 0;
start.pre = -1;
openTable.push(start);
while(!openTable.empty())
{
// 找到最优的一个状态
State preState = openTable.top(); // 获取状态索引
openTable.pop();
// 插入close表
preState.index = closeTable.size();
closeTable.push_back(preState);
// 已找到目标状态
if(preState.equal(goal))
{
return preState;
}
// 尝试向四个方向扩展该状态
for(int i(0); i < 4; ++i)
{
State cur = preState;
int zeroX = cur.fineZero(0);
int zeroY = cur.fineZero(1);
// 取得带扩展状态的空位位置
int goalX = direct[i][0]+zeroX;
int goalY = direct[i][1]+zeroY;
// 确保移动合法
if(isMove(goalX, goalY))
{
// 交换移动0
int num = cur.s[goalX][goalY];
cur.s[goalX][goalY] = 0;
cur.s[zeroX][zeroY] = num;
// 判重
if(!isCloseTableContain(cur))
{
// 将新状态置入状态序列
cur.g = f(cur);
cur.pre = preState.index;
// 插入open表
openTable.push(cur);
}
}
}
}
}
State inputData()//录入数据
{
State aNode;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
cin>>aNode.s[i][j];
}
}
return aNode;
}
//递归回溯求解
void disPlayWay(int index)
{
if(index == -1) return ;
State node = closeTable[index];
disPlayWay(node.pre);
node.Display();
cout<<"->"<<endl;
}
int main()
{
inital = inputData();
goal = inputData();
State result = AStar(inital);
cout<<"找到解答,移动步骤如下:"<<endl;
disPlayWay(result.index);
return 0;
}
/*
2 8 3
1 6 4
7 0 5
1 2 3
8 0 4
7 6 5
*/