基于图搜索技术的八数码问题求解C++

八数码,在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,
要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。

内容提要:
分别用广度优先搜索策略、深度优先搜索策略和启发式搜索算法(至少两种)求解八数码问题;
分析估价函数对启发式搜索算法的影响;探究讨论各个搜索算法的特点。

#include<bits/stdc++.h> 
using namespace std;
#define endl "\n" 
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;
} 


posted @ 2021-10-29 17:10  hrdate  阅读(735)  评论(0编辑  收藏  举报