问题描述:八数码拼图复原。
1 | 3 | |
4 | 2 | 5 |
7 | 8 | 6 |
复原为:
1 | 2 | 3 |
4 | 5 | 6 |
7 | 8 |
编程语言:C++
思想:
1.对八数码问题进行分析,空格处共有上下左右四种移动方式,故选择四叉树搜索解决该问题。
2.为了确保能寻得一个解,必须采用宽度优先搜索,即每一层每一层的搜索。
3.由于四叉树分支数为4^n,随着搜索深度加大,待搜索分支急剧上升,当解的路径较长时,搜索时间大幅上升,故选用启发式搜索。
应用的数据结构知识:树,队列,堆栈。
说明:代码封装在一个类中,不是一个完善类,仅仅做封装而已,在主程序中只创建一个实例就退出。并且动态分配的内存没有做回收。由于是初学C++并未使用STL。
代码:
SolvePicturePuzzle类:
class SolvePicturePuzzle {
private:
char SIZE[2], eachsize[2];//矩阵大小和分块大小
char** Start, ** Goal;//初始和目标矩阵
//char Depth;//最大深度
//-------------------------------------声明结构-------------------------------------
struct Tree {
char** data;
char sign;//作为当前操作标志
int value;//估价函数值
char depth;//当前深度
Tree* up;
Tree* down;
Tree* left;
Tree* right;
Tree* parent;
};
Tree* puzzle_Tree;//生成树
struct Stack {
char base[MAXSTACKLEN];
char* rear;//尾指针
};
Stack path;//路径
struct Nqueue {
Tree* N[MAXLEN];
int front, rear;
};
Nqueue Open, Closed;//以估价价值高低插入队列
//----------------------------------------------------------------------------------
public:
//-------------------------------------构造函数-------------------------------------
SolvePicturePuzzle(char S[][256], char G[][256], char row, char col, char er, char ec) {//初始矩阵,目标矩阵,矩阵行,矩阵列,分块行,分块列
Tree* answer;
char i = 0;
SIZE[0] = row; SIZE[1] = col; eachsize[0] = er; eachsize[1] = ec;
InitializeStack(path);
InitializeNqueue(Open);
InitializeNqueue(Closed);
Start = CreateMatrix(SIZE[0], SIZE[1]);//
Goal = CreateMatrix(SIZE[0], SIZE[1]);//初始化矩阵
Start = Assigned(Start, S, SIZE[0], SIZE[1]);
Goal = Assigned(Goal, G, SIZE[0], SIZE[1]);
puzzle_Tree = new Tree;
puzzle_Tree->data = Start; puzzle_Tree->depth = 1; puzzle_Tree->sign = NONE; puzzle_Tree->parent = NULL; puzzle_Tree->value = Evaluate(puzzle_Tree);
InNqueue(Open, puzzle_Tree);
//---------------------------------------以上内容为初始化-------------------------
cout << "Initial Matrix:" << endl;
DisplayMatrix(Start, SIZE[0], SIZE[1]);//显示主要信息
cout << "Goal Matrix:" << endl;
DisplayMatrix(Goal, SIZE[0], SIZE[1]);
//--------------------------------------------------------------------------------
answer = CreateTree(Open, Closed);//寻找解
if (answer != NULL) {//有解
cout << "Has an answer!" << endl;
FindPath(answer, path);//用堆栈存储路径
while (!StackisEmpty(path)) {//读取路径显示出来
i++;
switch (Pop(path)) {
case 1: {cout << "UP "; break; }
case 2: {cout << "DOWN "; break; }
case 3: {cout << "LEFT "; break; }
case 4: {cout << "RIGHT "; break; }
default: { break; }
}
}
cout << "(" << int(i) << ")" << endl;
}
else { cout << "No answer!" << endl; }
}
//----------------------------------------------------------------------------------
//-------------------------------------堆栈操作-------------------------------------
void InitializeStack(Stack& s) {//堆栈初始化
int i;
s.rear = s.base;
for (i = 0; i < MAXSTACKLEN; i++) { s.base[i] = '\0'; }
}
bool StackFlow(Stack& s) {//堆栈是否满
if (s.rear - s.base < MAXSTACKLEN) { return false; }
else return true;
}
bool StackisEmpty(Stack& s) {//栈空
if (s.rear == s.base)return true;
else return false;
}
void Push(Stack& s, char c) {//入栈
if (!StackFlow(s)) {
*s.rear = c;
s.rear++;
}
}
char Pop(Stack& s) {//出栈
if (!StackisEmpty(s)) {
s.rear = s.rear - 1;
return *s.rear;
}
return NULL;
}
char* StackGetPath(Stack& s) {//返回路径,启发式算法只有一条路径
char i;
char* p = new char[s.rear - s.base + 1];
p[0] = s.rear - s.base;//长度
for (i = 0; i < (s.rear - s.base); i++) {
p[i + 1] = s.base[i];
}
return p;
}
//----------------------------------------------------------------------------------
//-------------------------------------队列操作-------------------------------------
void InitializeNqueue(Nqueue& n) {//队列初始化,相当于清空队列
int i;
n.front = 0;
n.rear = n.front;
for (i = 0; i < MAXLEN; i++) { n.N[i] = NULL; }
}
bool Nqueueisempty(Nqueue& n) {//判断队空
if (n.rear == n.front)return true;
else return false;
}
bool Nqueueisfull(Nqueue& n) {//判断队满
if ((n.rear - n.front + MAXLEN) % MAXLEN == MAXLEN - 1)return true;
else return false;
}
bool InNqueue(Nqueue& n, Tree* t) {//插序入队
int i, j;
if (Nqueueisfull(n)) {//队满
cout << "Queue is full!" << endl;
return false;
}
for (i = n.front; (i + MAXLEN) % MAXLEN < n.rear; i++) {
if ((n.N[i % MAXLEN]->value) >= (t->value)) {
for (j = n.rear; (j + MAXLEN) % MAXLEN > i % MAXLEN; j--) {
n.N[(j + MAXLEN) % MAXLEN] = n.N[(j + MAXLEN - 1) % MAXLEN];
}
n.N[i % MAXLEN] = t;
n.rear = (n.rear + 1) % MAXLEN;
return true;
}
}
n.N[n.rear] = t;
n.rear = (n.rear + 1) % MAXLEN;
return true;
}
Tree* OutNqueue(Nqueue& n) {//出队
if (!Nqueueisempty(n)) {
n.front = (n.front + 1) % MAXLEN;
return n.N[(n.front - 1 + MAXLEN) % MAXLEN];
}
else { cout << "Queue is empty!" << endl; return NULL; }
}
void isExistInQueue(Tree* n, Nqueue& open, Nqueue& closed) {//判断该子节点是否在open表或closed表中。
int i, o, c;
bool O, C;//是否在open表中,closed表中。
O = false; C = false;
for (i = closed.front; ((i + MAXLEN) % MAXLEN) < closed.rear; i++) {
if (isEqual(n->data, closed.N[(i + MAXLEN) % MAXLEN]->data, SIZE[0], SIZE[1])) { C = true; c = i; break; }//子状态在closed表中
}
for (i = open.front; ((i + MAXLEN) % MAXLEN) < open.rear; i++) {
if (isEqual(n->data, open.N[(i + MAXLEN) % MAXLEN]->data, SIZE[0], SIZE[1])) { O = true; o = i; break; }//子状态在open表中
}
if ((!O) && (!C)) {//子状态不在open和closed表中
n->value = Evaluate(n);//估价
InNqueue(open, n);//将子状态加入到open表中
}
else if (O && (!C)) {//子状态在open表中
if (n->depth < open.N[(o + MAXLEN) % MAXLEN]->depth) {//子状态路径比open表中已有路径更短
n->value = Evaluate(n);//估价
InNqueue(closed, open.N[(o + MAXLEN) % MAXLEN]);//将更长路径的子状态加入closed表中。
for (i = o % MAXLEN; (i + MAXLEN) % MAXLEN < open.rear - 1; i++) {
open.N[(i + MAXLEN) % MAXLEN] = open.N[(i + MAXLEN + 1) % MAXLEN];
}//从open表中删除更长的路径
open.rear = (open.rear - 1 + MAXLEN) % MAXLEN;
InNqueue(open, n);//替换更短路径的子状态
}
else { InNqueue(closed, n); }//将子状态加入到closed表中
}
else if ((!O) && C) {//子状态在closed表中
if (n->depth < closed.N[(c + MAXLEN) % MAXLEN]->depth) {//子状态路径比closed表中已有路径更短
n->value = Evaluate(n);//估价
InNqueue(open, n);//将子状态加入到open表中
}
else { InNqueue(closed, n); }//将子状态加入到closed表中
}
}
//----------------------------------------------------------------------------------
//--------------------------------矩阵操作功能函数----------------------------------
char** CreateMatrix(char row, char col) {//创建矩阵并初始化
char i, j;
char** Matrix;
Matrix = new char* [row];
for (i = 0; i < row; i++) {
Matrix[i] = new char[col];
for (j = 0; j < col; j++) {
Matrix[i][j] = EMPTY;
}
}
return Matrix;
}
void ReleaseMatrix(char** Matrix, char row) {//删除矩阵释放空间
char i;
for (i = 0; i < row; i++) {
delete[]Matrix[i];
}
delete[]Matrix;
}
char** Assigned(char** Matrix, char** data, char row, char col) {//矩阵赋值,Matrix=data
char i, j;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
Matrix[i][j] = data[i][j];
}
}
return Matrix;
}
char** Assigned(char** Matrix, char data[][256], char row, char col) {//矩阵赋值重载
char i, j;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
Matrix[i][j] = data[i][j];
}
}
return Matrix;
}
bool isEqual(char** Matrix, char** data, char row, char col) {//判断两个矩阵是否相等
char i, j;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (data[i][j] != Matrix[i][j]) { return false; }
}
}
return true;
}
void DisplayMatrix(char** Matrix, char row, char col) {//显示矩阵
char i, j;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (Matrix[i][j] == EMPTY) { cout << setw(5) << " " << " "; continue; }
cout << setw(5) << int(Matrix[i][j]) << " ";
}
cout << endl << endl;
}
}
bool MatrixOperator(char** Matrix, char Operator) {//矩阵上下左右移动,如果可以移动则对应内存的数据已经被移动
char i, j, m, n, o, p;
char temp;
for (i = 0; i < SIZE[0]; i = i + eachsize[0]) {
for (j = 0; j < SIZE[1]; j = j + eachsize[1]) {//i,j一二层循环检索分块中的第一个元素
for (m = i; m < i + eachsize[0]; m++) {
for (n = j; n < j + eachsize[1]; n++) {//o,p三四层循环寻找空分块
if (Matrix[m][n] != EMPTY) { m = m + eachsize[0] + i; break; }//如果非空分块则跳出两层循环
if ((n == j + eachsize[1] - 1) && (m == i + eachsize[0] - 1)) {//此分块为空块
switch (Operator) {
case UP: {
if (m - eachsize[0] + 1 > 0) {//可进行上移操作
for (o = m - eachsize[0] + 1; o <= m; o++) {
for (p = n - eachsize[1] + 1; p <= n; p++) {
temp = Matrix[o][p];
Matrix[o][p] = Matrix[o - eachsize[0]][p];
Matrix[o - eachsize[0]][p] = temp;
}
}
return true;
}
else { return false; }
break; }
case DOWN: {
if (m != SIZE[0] - 1) {//可进行下移操作
for (o = m - eachsize[0] + 1; o <= m; o++) {
for (p = n - eachsize[1] + 1; p <= n; p++) {
temp = Matrix[o][p];
Matrix[o][p] = Matrix[o + eachsize[0]][p];
Matrix[o + eachsize[0]][p] = temp;
}
}
return true;
}
else { return false; }
break; }
case LEFT: {
if (n - eachsize[1] + 1 > 0) {//可进行左移操作
for (o = m - eachsize[0] + 1; o <= m; o++) {
for (p = n - eachsize[1] + 1; p <= n; p++) {
temp = Matrix[o][p];
Matrix[o][p] = Matrix[o][p - eachsize[1]];
Matrix[o][p - eachsize[1]] = temp;
}
}
return true;
}
else { return false; }
break; }
case RIGHT: {
if (n != SIZE[1] - 1) {//可进行右移操作
for (o = m - eachsize[0] + 1; o <= m; o++) {
for (p = n - eachsize[1] + 1; p <= n; p++) {
temp = Matrix[o][p];
Matrix[o][p] = Matrix[o][p + eachsize[1]];
Matrix[o][p + eachsize[1]] = temp;
}
}
return true;
}
else { return false; }
break; }
default: {return true; }//不做操作
}
}
}
}
}
}
return false;//矩阵无法移动
}
int GetMatrixDis(char** InitialMatrix, char** NextMatrix, char row, char col) {//统计矩阵对比目标矩阵的总距离
int sum = 0, temp = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
temp = 0;//清空该值,用于跳出循环
for (int o = 0; o < row; o++) {
if (temp != 0) { break; }
for (int p = 0; p < col; p++) {
if (InitialMatrix[o][p] == NextMatrix[i][j]) { temp = abs(o - i) + abs(p - j); break; }
}
}
sum = sum + temp;
}
}
return sum;
}
//----------------------------------------------------------------------------------
//---------------------------------启发式核心算法-----------------------------------
Tree* CreateTree(Nqueue& open, Nqueue& closed) {//启发式图搜索
while (!Nqueueisempty(open)) {
Tree* n = OutNqueue(open);//open表中第一位出队
/*cout << "Outqueue,sign:" << int(n->sign) << " depth:" << int(n->depth) << " value:" << int(n->value) << endl;*/
if (isEqual(n->data, Goal, SIZE[0], SIZE[1])) { n->up = NULL; n->down = NULL; n->left = NULL; n->right = NULL; return n; }//找到目标
n->up = CreateNode(n, n->data, n->depth, n->sign, UP);//生成所有子状态
n->down = CreateNode(n, n->data, n->depth, n->sign, DOWN);
n->left = CreateNode(n, n->data, n->depth, n->sign, LEFT);
n->right = CreateNode(n, n->data, n->depth, n->sign, RIGHT);
if ((n->up == NULL) && (n->down == NULL) && (n->left == NULL) && (n->right == NULL)) { continue; }//如果子状态全空,则继续下一次循环
JudgeState(n, open, closed);//对其子状态进行判别是否加入open表或closed表。
InNqueue(closed, n);//将该节点加入closed表中
}
return NULL;
}
Tree* CreateNode(Tree* parent, char** Matrix, char depth, char sign, char operation) {//创建子节点
Tree* Node;
char** Temp;
if ((((sign ^ operation) == 3) || ((sign ^ operation) == 7)) && (sign != 7)) { return NULL; }//上下两次操作重复,返回空节点
//if (depth + 1 > Depth) { return NULL; }//超出最大深度,返回空节点
Node = new Tree;
Temp = CreateMatrix(SIZE[0], SIZE[1]);//创建一个临时矩阵
Temp = Assigned(Temp, Matrix, SIZE[0], SIZE[1]);//将Matrix赋值给临时矩阵Temp
if (MatrixOperator(Temp, operation)) {//是否可以移动矩阵
Node->data = CreateMatrix(SIZE[0], SIZE[1]);//初始化Node->data
Node->data = Assigned(Node->data, Temp, SIZE[0], SIZE[1]);
ReleaseMatrix(Temp, SIZE[0]);//释放矩阵
Node->depth = depth + 1;
Node->sign = operation;
Node->parent = parent;
return Node;//移动完成,返回该节点
}
else { ReleaseMatrix(Temp, SIZE[0]); return NULL; }//无法移动,返回空节点
}
void JudgeState(Tree* n, Nqueue& open, Nqueue& closed) {//对子状态进行判别对子状态进行判别是否加入open表或closed表。
Tree* p;
p = n->up;
if (p != NULL) { isExistInQueue(p, open, closed); }//是否存在表中
p = n->down;
if (p != NULL) { isExistInQueue(p, open, closed); }
p = n->left;
if (p != NULL) { isExistInQueue(p, open, closed); }
p = n->right;
if (p != NULL) { isExistInQueue(p, open, closed); }
}
int Evaluate(Tree* n) {//估价函数
return (n->depth + GetMatrixDis(Goal, n->data, SIZE[0], SIZE[1]));
}
//----------------------------------------------------------------------------------
//------------------------------------寻找路径--------------------------------------
void FindPath(Tree* answer, Stack& p) {
if (answer->parent != NULL) {
Push(p, answer->sign);
FindPath(answer->parent, p);
}
}
//----------------------------------------------------------------------------------
};
主函数:
int main()
{
char S[256][256] = { { 1,0,3 },{ 4,2,5 },{ 7,8,6 } }, G[256][256] = { { 1,2,3 },{ 4,5,6 },{ 7,8,0 } };
SolvePicturePuzzle p(S, G, 3, 3, 1, 1);
return 0;
}