基於圖搜索技術的八數碼問題求解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;
} 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM