实验二 隐式图的搜索问题
实验二代码如下:
#include <vector>
#include <list>
#include <math.h>
#include <iostream>
using namespace std;
const int N = 3; //数组边长大小
struct node { //状态类
int data[N][N]; //九宫数据
int G, H, F; //3个估价函数值
node* parent; //前继指针
node() :G(0), H(0), F(0), parent(NULL) {} //默认构造函数
};
class Astar {
public:
Astar(int startmaze[N][N], int endmaze[N][N]); //初始化Astatr
list<node*> GetPath(); //获取全部路径
void Print(); //显示最佳路径的每一步状态
private:
bool isok(); //判断是否有解
node* findPath(); //获取最佳路径
vector<node*> getSurroundPoints(node* Sudoku) const; //获取0周围的可以移动获得的状态表
bool isInList(list<node*>& list, node point); //判断开启列表中是否包含某点
bool isSame(node* p, node point); //判断两个9宫内数据是否相同
node* getLeastFpoint(); //从开启列表中返回 F 值最小的状态指针
node* finallyload(); //在开启列表中寻找结束状态并返回,否则返回空
node* upSudoku(node*, int, int) const; //返回给定状态0上移的状态指针
node* downSudoku(node*, int, int) const; //返回给定状态0下移的状态指针
node* leftSudoku(node*, int, int) const; //返回给定状态0左移的状态指针
node* rightSudoku(node*, int, int) const; //返回给定状态0右移的状态指针
int GetG(node* Sudoku) { return Sudoku->parent == NULL ? 1 : Sudoku->parent->G + 1; } //从初始状态到指定状态的移动代价(如果是初始节点,则其父节点是空)
int GetH(node* Sudoku); //从指定状态到目标状态的估算成本
int GetF(node* Sudoku) { return GetH(Sudoku) + GetG(Sudoku); } //G,H之和
private:
node startSudoku; //初始九宫
node endSudoku; //目标九宫
list<node*> openList; //开启列表
list<node*> closeList; //关闭列表
list<node*> path; //最佳路径
};
Astar::Astar(int startmaze[N][N], int endmaze[N][N]) { //初始化初始九宫和目标九宫
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
startSudoku.data[i][j] = startmaze[i][j];
endSudoku.data[i][j] = endmaze[i][j];
}
}
}
bool Astar::isok() { //求出逆序对,判断是否有解
int a[9], k = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
a[k++] = startSudoku.data[i][j];
int sum = 0;
for (int i = 0; i < 9; i++)
for (int j = i + 1; j < 9; j++)
if (a[j] && a[i] && a[i] > a[j])
sum++;
return !(sum & 1); //由于目标解为偶数,所以状态的逆序数为偶数才可行,交换空格,逆序数增幅为偶数,故初始节点和目标的节点的逆序数奇偶性相同
}
list<node*> Astar::GetPath() { //根据路径链的头指针获取全部路径列表
node* result = findPath(); //路径链的头指针
while (result) {
path.push_front(result); //插入头部
result = result->parent;
}
// 清空临时开闭列表,防止重复执行 GetPath 导致结果异常
openList.clear();
closeList.clear();
return path;
}
void Astar::Print() { //显示最佳路径的每一步状态
GetPath(); //获取最佳路径
if (isok()) {
cout << "最优路径为:" << endl;
for (auto i : path) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
cout << i->data[j][k] << " ";
}
cout << endl;
}
cout << endl;
}
}
else cout << "无解!\n";
}
node* Astar::finallyload() { //在开启列表中寻找结束状态并返回,否则返回空
for (auto p : openList)
if (isSame(p, endSudoku))
return p;
return NULL;
}
node* Astar::findPath() { //获取最佳路径
node* p = &startSudoku;
openList.push_back(p); //置入初始状态,内外隔离
while (!openList.empty()) {
auto curPoint = getLeastFpoint(); //找到 F 值最小的点
openList.remove(curPoint); //从开启列表中删除
closeList.push_back(curPoint); //放到关闭列表
//1,找到当前周围4个格中可以移动的格子
auto surroundPoints = getSurroundPoints(curPoint);
for (auto& target : surroundPoints) //2,对某一个状态,如果它不在列表中,加入到开启列表,设置当状态为其父状态,计算 F
if (!isInList(openList, *target) && !isInList(closeList, *target)) {
target->parent = curPoint;
target->G = GetG(target); //从父结点按规则移动的距离
target->H = GetH(target);
target->F = GetF(target);
openList.push_back(target);
}
else {
int tempG = GetG(target);
if (tempG < target->G) { //判断是否有更优秀的到达该结点的路线
target->parent = curPoint;
target->G = tempG;
target->F = GetF(target);
}
}
if (isInList(openList, endSudoku)) //如果目标状态在开启列表中,结束搜索
return finallyload();
}
return NULL;
}
vector<node*> Astar::getSurroundPoints(node* Sudoku) const { //获取0周围的可以移动获得的状态表
vector<node*> test;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (Sudoku->data[i][j] == 0) {
if (i != 0)test.push_back(upSudoku(Sudoku, i, j));
if (i != N - 1)test.push_back(downSudoku(Sudoku, i, j));
if (j != 0)test.push_back(leftSudoku(Sudoku, i, j));
if (j != N - 1)test.push_back(rightSudoku(Sudoku, i, j));
return test;
}
}
}
return test;
}
bool Astar::isSame(node* p, node point) { //判断两个9宫内数据是否相同
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
if (p->data[i][j] != point.data[i][j])
return false;
return true;
}
bool Astar::isInList(list<node*>& list, node point) { //判断开启列表中是否包含某点
for (auto p : list)
if (isSame(p, point))
return true;
return false;
}
node* Astar::getLeastFpoint() { //从开启列表中返回 F 值最小的状态指针
if (!openList.empty()) {
auto resPoint = openList.front();
for (auto& point : openList)
if (point->F < resPoint->F)
resPoint = point;
return resPoint;
}
return NULL;
}
node* Astar::upSudoku(node* Sudoku, int a, int b) const { //返回给定状态0上移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a - 1][b]; //交换0和0上面的数据
p->data[a - 1][b] = 0;
return p;
}
node* Astar::downSudoku(node* Sudoku, int a, int b) const { //返回给定状态0下移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a + 1][b]; //交换0和0下面的数据
p->data[a + 1][b] = 0;
return p;
}
node* Astar::leftSudoku(node* Sudoku, int a, int b) const { //返回给定状态0左移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a][b - 1]; //交换0和0左面的数据
p->data[a][b - 1] = 0;
return p;
}
node* Astar::rightSudoku(node* Sudoku, int a, int b) const { //返回给定状态0左移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a][b + 1]; //交换0和0右面的数据
p->data[a][b + 1] = 0;
return p;
}
int Astar::GetH(node* Sudoku) { //从指定状态到目标状态的估算成本
int h = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (Sudoku->data[i][j] != 0)
h += abs(Sudoku->data[i][j] / N - i) + abs(Sudoku->data[i][j] % N - j);
}
}
return h; //返回所有数字距离它目标位置的最短距离之和
}
int main() {
int startmaze[N][N] = { {1,2,3},{4,5,6},{7,8,0} }; //初始九宫数据
int endmaze[N][N] = { {0,1,2},{3,4,5},{6,7,8} }; //目标九宫数据
Astar astar(startmaze, endmaze);
astar.Print(); //输出最佳路径
system("pause");
return 0;
}