小时候玩过的移动拼图游戏。有一个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层数大了之后,性能变得很差。
本文介绍了使用BFS解决移动拼图游戏(八数码问题)的方法。通过建立节点结构,利用BFS寻找从初始状态到目标状态的最佳移动路径。程序在特定示例中分别用2秒和137秒找到了14步和28步的变换路径,显示了BFS在处理不同难度问题时的性能差异。
 BFS版&spm=1001.2101.3001.5002&articleId=47264355&d=1&t=3&u=a0b1e335e6d1444e8dcd58aa98ac7419)
1291

被折叠的 条评论
为什么被折叠?



