移动拼图游戏(八数码问题) BFS版

本文介绍了使用BFS解决移动拼图游戏(八数码问题)的方法。通过建立节点结构,利用BFS寻找从初始状态到目标状态的最佳移动路径。程序在特定示例中分别用2秒和137秒找到了14步和28步的变换路径,显示了BFS在处理不同难度问题时的性能差异。

小时候玩过的移动拼图游戏。有一个3*3的棋盘,其中有0-8这9个数字,0表示空格,每次移动只能把空格旁边的数字移到空格,即与0相邻的数字可以和0交换位置。

求从初始状态

2 3 0

7 1 6

5 8 4

变到目标状态

1 2 3

4 5 6

7 8 0

的最佳移动方案。


今天先用BFS练练手。

每个结点用一个长度为9的数组记录当前矩阵;用blankPos变量记录空格位置,便于计算出相邻结点;用parent指针指向BFS过程中的父节点,用于找到目标时回溯出路径。

起始结点先入队,用set将其标记为访问过。每次从队列中取出一个结点,计算出其相邻结点(可能是2个、3个、4个),若其相邻结点未访问过,则入队,并用set将其标记为访问过。


/*2015.8.3cyq*/
//八数码BFS
#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <time.h>
using namespace std;

struct Node{
	vector<int> data;
	int blankPos;//空格(用0表示)位置,用于计算出相邻结点
	Node *parent;
};
//检测结点是否相等
bool isEqual(Node *&a,Node *&b){
	for(int i=0;i<9;i++)
		if(a->data[i]!=b->data[i])
			return false;
	return true;
}
//检测是否有解
bool canSolve(Node *&a,Node *&b){
	int sum1=0,sum2=0;
	for(int i=0;i<9;i++)
		for(int j=i+1;j<9;j++){
			if(a->data[i] > a->data[j]&&a->data[j]!=0)
				sum1++;
			if(b->data[i] > b->data[j]&&b->data[j]!=0)
				sum2++;
		}

	return (sum1%2==sum2%2);//奇偶性相同才有解
}
//显示结点的数据矩阵
void Show(Node *&a){
	if(a==nullptr)
		return;
	for(int i=0;i<9;i++){
		if(a->data[i]==0)
			cout<<"  ";
		else
		    cout<<a->data[i]<<" ";
		if((i+1)%3==0)
			cout<<endl;
	}
}
//空格上移后的结点
Node* upNode(Node *&a){
	if(a->blankPos<=2)
		return nullptr;
	else{
		Node* tmp=new Node(*a);
		swap(tmp->data[tmp->blankPos],tmp->data[tmp->blankPos-3]);
		tmp->blankPos-=3;
		return tmp;
	}
}
//空格下移后的结点
Node* downNode(Node *&a){
	if(a->blankPos>=6)
		return nullptr;
	else{
		Node* tmp=new Node(*a);
		swap(tmp->data[tmp->blankPos],tmp->data[tmp->blankPos+3]);
		tmp->blankPos+=3;
		return tmp;
	}
}
//空格左移后的结点
Node* leftNode(Node *&a){
	if(a->blankPos%3==0)
		return nullptr;
	else{
		Node* tmp=new Node(*a);
		swap(tmp->data[tmp->blankPos],tmp->data[tmp->blankPos-1]);
		tmp->blankPos--;
		return tmp;
	}
}
//空格右移后的结点
Node* rightNode(Node *&a){
	if(a->blankPos%3==2)
		return nullptr;
	else{
		Node* tmp=new Node(*a);
		swap(tmp->data[tmp->blankPos],tmp->data[tmp->blankPos+1]);
		tmp->blankPos++;
		return tmp;
	}
}
int main(){
	clock_t time1,time2;//用于计算程序运行时间
	time1=clock();
	int ivec1[10]={2,3,0,
				   7,1,6,
				   5,8,4};//起始结点
	//int ivec1[10]={0,8,7,
	//			     6,5,4,
	//		         3,2,1};//起始结点
	int ivec2[10]={1,2,3,
		           4,5,6,
				   7,8,0};//目标结点
	Node a,b;
	for(int i=0;i<9;i++){//9代表空字符
		a.data.push_back(ivec1[i]);
		b.data.push_back(ivec2[i]);
	}
	for(int i=0;i<9;i++){
		if(a.data[i]==0)
			a.blankPos=i;
		if(b.data[i]==0)
			b.blankPos=i;
	}
	a.parent=nullptr;
	b.parent=nullptr;
	Node* startNode=&a;
	Node* targetNode=&b;

	if(!canSolve(startNode,targetNode)){
		cout<<"Can't solve!"<<endl;
		return 0;
	}
	queue<Node*> q;
	q.push(startNode);
	set<vector<int> > visited;
	visited.insert(startNode->data);
	while(!q.empty()){
		Node* root=q.front();
		q.pop();
		if(isEqual(root,targetNode)){
			targetNode->parent=root->parent;
			break;
		}
		Node* node1=upNode(root);
		Node* node2=downNode(root);
		Node* node3=leftNode(root);
		Node* node4=rightNode(root);
		if(node1!=nullptr&&visited.find(node1->data)==visited.end()){
			node1->parent=root;
			visited.insert(node1->data);
			q.push(node1);
		}
		if(node2!=nullptr&&visited.find(node2->data)==visited.end()){
			node2->parent=root;
			visited.insert(node2->data);
			q.push(node2);
		}
		if(node3!=nullptr&&visited.find(node3->data)==visited.end()){
			node3->parent=root;
			visited.insert(node3->data);
			q.push(node3);
		}
		if(node4!=nullptr&&visited.find(node4->data)==visited.end()){
			node4->parent=root;
			visited.insert(node4->data);
			q.push(node4);
		}
	}
	//利用结点的parent指针回溯出路径
	Node* p=targetNode;
	vector<Node*> result;
	while(p!=nullptr){
		result.push_back(p);
		p=p->parent;
	}
	int count=0;
	for(int i=result.size()-1;i>=0;i--){
		cout<<"stage:"<<count++<<endl;
		Show(result[i]);
	}
	time2=clock();
	cout<<"程序运行耗费的毫秒数:"<<(time2-time1)<<endl;
	return 0;
}



测试后发现,从初始状态

2 3 0

7 1 6

5 8 4

变到目标状态

1 2 3

4 5 6

7 8 0

我的程序用2秒钟可以输出具体步骤,经历14次变换。

而从初始状态

0 8 7

6 5 4

3 2 1

变到目标状态

1 2 3

4 5 6

7 8 0

两个状态差距较大。需要137秒才能输出结果,经历28次变换。可见BFS层数大了之后,性能变得很差。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值