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

来源:互联网 发布:读书app 知乎 编辑:程序博客网 时间:2024/06/05 07:27

小时候玩过的移动拼图游戏。有一个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层数大了之后,性能变得很差。

0 1
原创粉丝点击