八数码,在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,
要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。
内容提要:
分别用广度优先搜索策略、深度优先搜索策略和启发式搜索算法(至少两种)求解八数码问题;
分析估价函数对启发式搜索算法的影响;探究讨论各个搜索算法的特点。
#include<bits/stdc++.h>
using namespace std;
#define endl "
"
const int INF=0x3f3f3f3f;
struct Status{
int gx; //实际已走代价
int fx; //估值
string sta; //长度为9的字符串表示状态
bool operator < (const Status& t) const{
return fx > t.fx;
}
}initial,target;
unordered_map<string,int>close; //记录已遍历的状态
const int LAYER_MAX=20; //dfs最大深度
int MAX_COST=0; //遍历到目标状态的代价
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}}; //下一状态可能的改变
//输出显示当前状态
void show(const Status& t){
cout<<"当前状态gx="<<t.gx<<" fx="<<t.fx<<endl;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cout<<t.sta[i*3+j];
}
cout<<endl;
}
}
//计算估值函数
int get_hx(const Status& t){
int hx=0;
for(int i=0;i<9;i++)
if(target.sta[i]!=t.sta[i]) //计算位置不同的个数
hx++;
return hx;
}
void init(){
// 初始状态
cout<<"请输入初始状态:"<<endl;
initial.sta="";
initial.gx=0;
initial.fx=get_hx(initial)+initial.gx;
vector<int>used(10,0);
for(int i=0;i<9;i++){
int index=0;
while(1){
index=rand()%9;
if(!used[index])break;
}
if(index==0)initial.sta+=' ';
else initial.sta+=char('0'+index);
used[index]=1;
}
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
cout<<initial.sta[i*3+j];
}
cout<<endl;
}
//cout<<"请输入目标状态:"<<endl;
target.sta="1238 4765";
target.gx=INF;
target.fx=INF;
}
int bfs(){
close.clear();
queue<string>open;
open.push(initial.sta);
close[initial.sta]=0;
while(!open.empty()){
string now=open.front();
open.pop();
//show(now);
if(now==target.sta){
return close[target.sta]; //到达目标状态
}
int dis=close[now];
int index;
for(int i=0;i<9;i++){
if(now[i]==' '){
index=i;
break;
}
}
int row=index/3,colum=index%3; //一维坐标转化成二维
for(int i=0;i<4;i++){
int trow=row+dir[i][0]; //假设空白处移动
int tcolum=colum+dir[i][1];
if(trow>=0&&trow<=2&&tcolum>=0&&tcolum<=2){ //判断移动后的坐标是否合理
string next=now;
swap(next[trow*3+tcolum],next[index]); //真实移动空白处
if(!close.count(next)){ //判断当前状态是否遍历过
close[next]=dis+1;
open.push(next);
MAX_COST++;
}
}
}
}
return -1;
}
void dfs(string now,int row,int colum,int layer_num){
if(!close.count(now)) close[now]=layer_num; //贪心获取较小的可行解
else {
if(close[now]<layer_num) return ;
else close[now]=layer_num;
}
if(layer_num>=LAYER_MAX){ //超过搜索层数
return ;
}
if(now==target.sta){ //达到目标状态
return ;
}
//往上下左右四个方向进行扩展
for(int i=0;i<4;i++){
int trow=row+dir[i][0];
int tcolum=colum+dir[i][1];
if(trow>=0&&trow<=2&&tcolum>=0&&tcolum<=2){ //选择合理状态
swap(now[trow*3+tcolum],now[row*3+colum]);
dfs(now,trow,tcolum,layer_num+1);
MAX_COST++;
swap(now[trow*3+tcolum],now[row*3+colum]);
}
}
}
void astar(){
close.clear();
close[target.sta]=INF;
priority_queue<Status>open;
open.push(initial);
while(!open.empty()){
Status now=open.top();
open.pop();
close[now.sta]=now.fx;
//show(now); //输出当前状态
if(!get_hx(now))break; //到达目标状态
//找到空白处坐标
int row,colum;
for(int i=0;i<9;i++){
if(now.sta[i]==' '){
row=i/3; colum=i%3; //一维坐标转化成二维
break;
}
}
for(int i=0;i<4;i++){
//假设空白处移动
int trow=row+dir[i][0];
int tcolum=colum+dir[i][1];
//判断移动后的坐标是否合理
if(trow>=0&&trow<=2&&tcolum>=0&&tcolum<=2){
Status next=now;
next.gx++;
next.fx=next.gx+get_hx(next);
//真实移动空白处
swap(next.sta[trow*3+tcolum],next.sta[row*3+colum]);
//启发判断下一状态
if(!close.count(next.sta)||close[next.sta]>=next.fx){
open.push(next);
MAX_COST++;
}
}
}
}
}
int main(){
srand(time(NULL)); //时间种子
init();
int x,y; //获取初始状态的空格位置
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(initial.sta[i*3+j]==' '){
x=i;
y=j;
break;
}
}
}
cout<<"广度优先搜索bfs算法:"<<endl;
int ans=bfs();
cout<<"到目标状态广搜代价:"<<MAX_COST<<endl;
cout<<"到目标状态最小代价:";
cout<<(ans==-1?"不可解":to_string(ans))<<endl;
//进到深度搜索
cout<<"深度优先搜索dfs算法:"<<endl;
close.clear(); //初始化close表
close[target.sta]=INF;
MAX_COST=0;
dfs(initial.sta,x,y,0);
cout<<"到目标状态深搜代价:"<<MAX_COST<<endl;
cout<<"到目标状态最小代价:";
cout<<(close[target.sta]==INF?"不可解":to_string(close[target.sta]))<<endl;
//进到启发式搜索
cout<<"启发搜索A*算法:"<<endl;
MAX_COST=0;
astar();
cout<<"到目标状态广搜代价:"<<MAX_COST<<endl;
cout<<"到目标状态最小代价:";
cout<<(close[target.sta]==INF?"不可解":to_string(close[target.sta]))<<endl;
return 0;
}