八数码问题:C++广度搜索实现

毕竟新手上路23333,有谬误还请指正。 课程设计遇到八数码问题(这也是一坨),也查过一些资料并不喜欢用类函数写感觉这样规模小些的问题没有必要,一开始用深度搜索却发现深搜会陷入无底洞,如果设定了深度限制又会有很多情况无法找到,然后果断放弃,改用广度搜索。  如果要改善代码效率还可以用双向搜索,即从起始状态和最终状态同时搜索,找到相同状态,这样搜索时间会缩短一半。此外还可以用A*(启发式算法)会显得高端很多。

题目要求: 在一个3*3的棋盘上,随机放置1到8的数字棋子,剩下一个空位。数字可以移动到空位(编程时,空位可用0代替,且可以理解为是空位的上、下、左、右移动),经过若干次移动后,棋局到达指定目标状态。 数组表示棋局状态。用函数表示空格(0)的移动,使用函数具有前提条件,满足条件才可以使用。 用广度优先或深度优先搜索求解。还可以使用启发式求解,以提高搜索效率。 要求: ① 编程求解问题; ② 给出中间状态; ③ 给出解序列(函数调用序列)

 #include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <sstream>
#include <deque>
#include <functional>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <stack>
#include <set>
#include <numeric>
#include <utility>
#include <cstring>
#include <fstream> using namespace std;
int board[]; //初始状态,一维数组模拟二维
int destboard[]; //目标状态
int dx[] = {-, , , }; //数字0的移动偏移量
int dy[] = {, , , -};
map<int, bool> mark; //记录已搜索的状态 int lists(int i, int j)
{
return i* + j; //返回i, j二维数组的一维位置
} int judge() //运算前判断是否可以找到一种对于初末状态可行的变换
{
int first_d[],last_d[],i,j=,k,rank1=,rank2=;
for(i=;i<=;i++) //初状态赋值给first_d[10]
{
while()
{
if(j==){j=;}
if(j==){j=;}
first_d[i]=destboard[j];
j++;
break;
}
}
j=;i=;
for(k=;k<=;k++) //最终状态赋值给last_d[10]
{
while()
{
last_d[k]=board[lists(i, j)];
j++;
if(j==){i++;j=;}
break;
}
} for(j=;j<=;j++) //计算初状态的奇偶性
{
for(i=;i<j;i++)
{
if(first_d[i]>first_d[j]&&first_d[i]!=&&first_d[j]!=){rank1++;}
}
} for(j=;j<=;j++) //计算末状态的奇偶性
{
for(i=;i<j;i++)
{
if(last_d[i]>last_d[j]&&last_d[i]!=&&last_d[j]!=){rank2++;}
}
}
int a1=rank1%,a2=rank2%;
if(a1!=a2){return ;} //判断奇偶性是否相同,相同才可以从出状态变到末状态
else{return ;}
} struct Stat //结构体三个参数
{
int step; // 步数
int board[]; // 状态
Stat(int *b, int s=)
{
memcpy(this->board, b, sizeof(int)*); //对状态的赋值操作
step = s;
}
}; bool ok(int *b) // 判断是否到已经达目标状态
{
for(int i=; i<=; ++i)
for(int j=; j<=; ++j)
{
if(b[lists(i, j)] != destboard[lists(i, j)])
return false;
}
return true;
} int Bfs()
{
int judge_first=judge();
ofstream ofs; //建立数据外存文件
ofs.open("output.dat");
if(judge_first==){cout<<"不存在"<<endl;return ;}
if(judge_first==)
{
queue<Stat> que; //建队que
que.push(Stat(board, )); // 初始状态入队
while(que.size())
{
int m=, mk=; // 记录状态m,以及基数mk
Stat p = que.front(); //取出队头元素
for(int i=; i<=; ++i)
{
for(int j=; j<=; ++j)
{
ofs<<p.board[lists(i, j)]<<" ";
}
ofs<<endl;
}
ofs<<endl; que.pop(); //出队 if(ok(p.board))
{
return p.step; // 到达目标则返回最短步数
} for(int i=; i<=; ++i) // 这个是为了标记初始状态,不能遗漏
for(int j=; j<=; ++j)
{
m+=p.board[lists(i, j)]*mk;
mk*=;
}
if(!mark.count(m)) // 未标记则标记
mark[m] = true; for(int k=; k<; ++k) // 四个方向搜索
{
Stat n(p.board, p.step+); // n是下一步搜索的状态
int zx, zy; // zx,zy存放当前状态0的位置 for(int i=; i<=; ++i) // 搜索当前状态的0的位置
for(int j=; j<=; ++j)
{
if(p.board[lists(i,j)]==)
{
zx = i;
zy = j;
break;
}
if(p.board[lists(i,j)]==)
break;
} int nx = zx+dx[k]; //下一个状态的0的位置
int ny = zy+dy[k];
m = ; //标记状态
mk = ;
swap(n.board[lists(nx,ny)],n.board[lists(zx, zy)]); //交换 for(int i=; i<=; ++i)
for(int j=; j<=; ++j)
{
m+=n.board[lists(i, j)]*mk;
mk*=;
}
if(!mark.count(m))
{
mark[m] = true;
que.push(n); //若未搜索过,则入队
}
}
}
ofs.close(); //结束外存
return ;
}
return ;
} int main()
{
cout<<"广度搜索八数码问题:\n";
cout<<"请输入初始状态用0-8表示\n";
memset(board, , sizeof(board));
for(int i=; i<=; ++i)
for(int j=; j<=; ++j)
scanf("%1d", &board[lists(i, j)]); cout<<"请输入结束状态\n";
for(int m=;m<=;m++){scanf("%d",&destboard[m]);}
for(int m=;m<=;m++){scanf("%d",&destboard[m]);}
for(int m=;m<=;m++){scanf("%d",&destboard[m]);}
mark.clear(); cout<<"准备搜索...\n";
system("pause");
cout<<"搜索中.....\n"; int t=Bfs();
cout<<"搜索完毕,过程已经以文件的形式存储\n";
if(t==){cout<<"没有找到"<<endl;}
else{cout<<"深度为:"<<t<< endl;}
system("pause");
return ;
}

输入初始状态和最终状态,中间过程会生成在”output.dat”中。

上一篇:845. 八数码(bfs+map)


下一篇:Thinkphp文件上传