八数码问题

问题描述:八数码拼图复原。

 

1 3
425
786

复原为:

123
456
78 

 

编程语言: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;
}

结果:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值