数据结构算法: 八数码问题 小结

来源:互联网 发布:旅游险怎么买 知乎 编辑:程序博客网 时间:2024/06/02 01:27

  • 问题定义
  • 算法流程
  • 相关搜索策略
    • 单向BFS搜索
    • 双向BFS搜索
    • 逆向BFS离线打表
    • A启发式搜索算法
    • 八数码问题变种解法 hdu3567

好久没写博客了,最近开始更一个系列,关于数据结构算法的,希望能形成一个好的体系。 之前写的博文是零碎的知识点小结,只适合自己回顾,不适合他人阅读或者教学,希望这个系列能改善相关的问题,方便他人阅读,分享知识。

问题定义

  • 八数码问题: 在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
  • 八数码问题一般使用搜索法来解。搜索法有广度优先搜索法、双向广度优先算法、深度优先搜索法、A*算法等。这里通过用不同方法解八数码问题来比较一下不同搜索法的效果。关于搜索问题,往往重要的是相关的搜索策略的选择。策略选择的好,效率就会有非常大的差异。

  • oj题目:PKU(1077) HDOJ(1043) ZOJ(1217) 由于测试数据的不同各平台状态不同

  • 变种:hdu 3567等

算法流程:

  1. cantor算法+奇偶性判断(或者逆序对数目判断)是否存在解,若有解,转2; 否则退出
  2. 根据环境选择搜索策略进行搜索求解,本文以典型的搜索算法: BFS,双向BFS, 离线算法,A*算法为例进行讲解

相关搜索策略

单向BFS搜索

#include<iostream>#include<cstdio>#include<cstring>//#include<vector>#include<algorithm>#include<queue>#include<map>#ifdef L_JUDGE#pragma warning(disable:4996)#endifusing namespace std;typedef pair<int,char> pic;struct Node{    int s[9];    int ri,ci;    int status;    int cnt;    Node(){        memset(s,0,sizeof(s));        ri=ci=0;        status=-1;        cnt=0;    }};const int dir[4][2]={-1,0,1,0,0,-1,0,1};char in[20];Node st,ed;queue<Node> Q;int fac[9];bool vis[(int)4e5+10];map<int,pair<int,int> >pre;bool InMap(int ri,int ci){    return ri>=0&&ri<3&&ci>=0&&ci<3;}int Cantor(int s[]){     int ret=0;     for(int i=0;i<9;i++){         int cnt=0;         for(int j=i+1;j<9;j++){             if(s[j]<s[i])cnt++;         }         ret+=cnt*fac[8-i];     }     return ret;}int BFS(const Node& st,int edval){    memset(vis,false,sizeof(vis));    while(!Q.empty())Q.pop();    pre.clear();    Q.push(st);    vis[st.status]=true;    pre[st.status]=make_pair(-1,-1);    while(!Q.empty()){        Node u=Q.front();        Q.pop();        if(u.status==edval)return u.cnt;        for(int di=0;di<4;di++){             Node v=u;             v.ri=u.ri+dir[di][0];             v.ci=u.ci+dir[di][1];             if(!InMap(v.ri,v.ci))continue;             int upos=3*u.ri+u.ci;             int vpos=3*v.ri+v.ci;             swap(v.s[upos],v.s[vpos]);             v.status=Cantor(v.s);             if(vis[v.status])continue;             vis[v.status]=true;             v.cnt=u.cnt+1;             Q.push(v);             pre[v.status]=make_pair(u.status,di);             if(v.status==edval)return v.cnt;        }    }    return -1;}int main(){    #ifdef L_JUDGE        freopen("in.txt","r",stdin);//      freopen("out.txt","w",stdout);    #endif    fac[0]=1;    for(int i=1;i<9;i++)fac[i]=i*fac[i-1];    ed.ri=ed.ci=2;    for(int i=0;i<9;i++){        ed.s[i]=i+1;    }    ed.status=Cantor(ed.s);    while(cin>>in[0]){        for(int i=1;i<9;i++)cin>>in[i];        for(int ri=0;ri<9;ri++){            if('x'==in[ri]){                st.s[ri]=9;                st.ri=ri/3;                st.ci=ri%3;            }            else st.s[ri]=in[ri]-'0';        }        st.status=Cantor(st.s);        st.cnt=0;        int flag=BFS(st,ed.status);        if(-1==flag)puts("unsolvable");        else{            char ans[1000];            char ch[5]="udlr";            int k=ed.status;            ans[flag]=0;            while(k!=st.status){                flag--;                ans[flag]=ch[pre[k].second];                k=pre[k].first;            }            puts(ans);        }    }    #ifdef L_JUDGE        fclose(stdin);        fclose(stdout);//      system("out.txt");    #endif    return 0;}

双向BFS搜索

#include<iostream>#include<cstdio>#include<cstring>//#include<vector>#include<algorithm>#include<queue>#ifdef L_JUDGE#pragma warning(disable:4996)#endifusing namespace std;const int MAXN=5e5;const int MAXM=6;const int MAXS=1025;const int MOD=1e9+7;char visited[MAXN];int father1[MAXN];int father2[MAXN];int move1[MAXN];int move2[MAXN];struct Status{     char eight[10];     int space;     int status;};Status s,s1,s2,t;queue<Status> Q1;queue<Status> Q2;bool found;int status;int fac_n[]={1,1,2,6,24,120,720,5040,40320,362880};const int dir[4][2]={-1,0,1,0,0,-1,0,1};int Cantor(char s[]){     int ret=0;     for(int i=0;i<9;i++){         int cnt=0;         for(int j=i+1;j<9;j++){             if(s[i]>s[j])cnt++;         }         ret+=cnt*fac_n[9-1-i];     }     return ret;}bool bSolve(char s[]){     int cnt=0;     for(int i=0;i<9;i++){         if(s[i]==9)continue;         for(int j=i+1;j<9;j++){             if(s[j]==9)continue;             if(s[i]>s[j])cnt++;         }     }     if(1&cnt)return false;     else return true;}bool InMap(int ri,int ci){    return ri>=0&&ri<3&&ci>=0&&ci<3;}void BFS_expand(queue<Status> &Q,bool flag){     int k,x,y;     s=Q.front();     Q.pop();     k=s.space;     x=k/3;y=k%3;     for(int di=0;di<4;di++){          int xx=x+dir[di][0];          int yy=y+dir[di][1];          if(!InMap(xx,yy))continue;          t=s;          t.space=3*xx+yy;          swap(t.eight[t.space],t.eight[s.space]);          t.status=Cantor(t.eight);          if(flag){              if(visited[t.status]!=1//&&(bSolve(t.eight))                      ){                  move1[t.status]=di;                  father1[t.status]=s.status;                  if(visited[t.status]==2){                      found=true;                      status=t.status;                      return;                  }                  visited[t.status]=1;                  Q.push(t);              }          }else{              if(visited[t.status]!=2//&&(bSolve(t.eight))                      ){                  move2[t.status]=di;                  father2[t.status]=s.status;                  if(visited[t.status]==1){                      found=true;                      status=t.status;                      return;                  }                  visited[t.status]=2;                  Q.push(t);              }          }     }}void TBFS(){     memset(visited,0,sizeof(visited));     while(!Q1.empty())Q1.pop();     while(!Q2.empty())Q2.pop();     found=false;     visited[s1.status]=1;     visited[s2.status]=2;     father1[s1.status]=-1;     father2[s2.status]=-1;     Q1.push(s1);Q2.push(s2);     while((!Q1.empty()) ||(!Q2.empty())){         if(!Q1.empty())BFS_expand(Q1,true);          if(found)return;          if(!Q2.empty())BFS_expand(Q2,false);          if(found)return;     }}void PrintPath1(int father[],int move[]){     int n,u;     char path[1000];     n=0;     u=status;     while(father[u]!=-1){         path[n++]=move[u];         u=father[u];     }     for(int i=n-1;i>=0;i--){         switch(path[i]){             case 0:                 putchar('u');                 break;             case 1:                 putchar('d');                 break;             case 2:                 putchar('l');                 break;             case 3:                 putchar('r');                 break;         }     }}void PrintPath2(int father[],int move[]){     int n,u;     char path[1000];     n=0;     u=status;     while(father[u]!=-1){         path[n++]=move[u];          u=father[u];     }     for(int i=0;i<n;i++){         switch(path[i]){             case 0:                 putchar('d');                 break;             case 1:                 putchar('u');                 break;             case 2:                 putchar('r');                 break;             case 3:                 putchar('l');                 break;         }     }}int main(){    #ifdef L_JUDGE        freopen("in.txt","r",stdin);//      freopen("out.txt","w",stdout);    #endif    char ch;    while(cin>>ch){        if(ch=='x'){            s1.eight[0]=9;            s1.space=0;        }else{            s1.eight[0]=ch-'0';        }        for(int i=1;i<9;i++){             cin>>ch;             if(ch=='x'){                 s1.eight[i]=9;                 s1.space=i;             }else{                 s1.eight[i]=ch-'0';             }        }        s1.status=Cantor(s1.eight);        for(int i=0;i<9;i++){            s2.eight[i]=(i+1);        }        s2.space=8;        s2.status=Cantor(s2.eight);        if(!bSolve(s1.eight)){             puts("unsolvable");        }else{            TBFS();            if(found){                if(s1.status!=status)PrintPath1(father1,move1);                if(s2.status!=status)PrintPath2(father2,move2);                putchar('\n');            }else                puts("unsolvable");        }    }    #ifdef L_JUDGE        fclose(stdin);        fclose(stdout);//      system("out.txt");    #endif    return 0;}

逆向BFS+离线打表

#include<iostream>#include<cstdio>#include<cstring>//#include<vector>#include<algorithm>#include<queue>#ifdef L_JUDGE#pragma warning(disable:4996)#endifusing namespace std;const int MAXN=5e5+10;const int MAXM=6;const int MAXS=1025;const int MOD=1e9+7;const int dir[4][2]={-1,0,1,0,0,-1,0,1};struct Node{     public:         int s[9];         int status;         int ri,ci;         Node(){              ri=ci=0;              status=0;              memset(s,0,sizeof(s));         }}st,ed;int fac_n[10];pair<int,int> path[MAXN];bool vis[MAXN];int Cantor(int s[]){    int ret=0;    for(int i=0;i<9;i++){        int cnt=0;        for(int j=i+1;j<9;j++){            if(s[i]>s[j])cnt++;        }        ret+=fac_n[9-i-1]*cnt;    }    return ret;}bool InMap(int ri,int ci){     return ri>=0&&ri<3&&ci>=0&&ci<3;}void BFS(){    for(int i=0;i<9;i++){        ed.s[i]=i+1;    }    ed.ri=ed.ci=2;    ed.status=Cantor(ed.s);    memset(vis,false,sizeof(vis));    memset(path,-1,sizeof(path));    queue<Node> Q;    Q.push(ed);    vis[ed.status]=true;    path[ed.status]=make_pair(-1,-1);    while(!Q.empty()){         Node u=Q.front();         Q.pop();         for(int di=0;di<4;di++){             Node v=u;             v.ri=u.ri+dir[di][0];             v.ci=u.ci+dir[di][1];             if(!InMap(v.ri,v.ci))continue;             swap(v.s[3*v.ri+v.ci],v.s[3*u.ri+u.ci]);             v.status=Cantor(v.s);             if(vis[v.status])continue;             vis[v.status]=true;             path[v.status]=make_pair(u.status,di);             Q.push(v);         }    }}char chd[10]="durl";char ans[MAXN],anslen;bool Print(int s,int t){    if(s==t)return true;    if(s==-1)return false;    ans[anslen++]=chd[path[s].second];    bool flag=Print(path[s].first,t);    return flag;}int main(){    #ifdef L_JUDGE        freopen("in.txt","r",stdin);//      freopen("out.txt","w",stdout);    #endif    fac_n[0]=fac_n[1]=1;    for(int i=2;i<=9;i++)fac_n[i]=i*fac_n[i-1];    BFS();    char input[50];    while(cin.getline(input,50,'\n')){        int si=0;        for(int i=0;input[i];i++){            if(input[i]!=' '){                if(input[i]=='x'){                    st.ri=si/3;                    st.ci=si%3;                    st.s[si]=9;                }else{                    st.s[si]=input[i]-'0';                }                si++;            }        }        st.status=Cantor(st.s);        int k=st.status;        anslen=0;        memset(ans,0,sizeof(ans));        bool flag=Print(st.status,ed.status);        if(!flag)puts("unsolvable");        else puts(ans);    }    #ifdef L_JUDGE        fclose(stdin);        fclose(stdout);//      system("out.txt");    #endif    return 0;}

A*启发式搜索算法

#include<iostream>#include<cstdio>#include<cstring>//#include<vector>#include<algorithm>#include<cstdlib>#include<queue>#ifdef L_JUDGE#pragma warning(disable:4996)#endifusing namespace std;const int MAXN=5e5+10;const int MAXM=6;const int MAXS=1025;const int MOD=1e9+7;struct Node{    public:    int s[9];    int ri,ci;    int status;    int f,g,h;    Node(){        ri=0;ci=0;        memset(s,0,sizeof(s));        status=0;    }    bool operator<(const Node& p) const{        if(f<p.f)return false;        else if((f==p.f)&&(h<p.h))return false;        return true;    }};const int dir[4][2]={-1,0,1,0,0,-1,0,1};const char d[10]="udlr";char input[50];int fac_n[10];Node st,ed;bool vis[MAXN];void Init(){    fac_n[0]=fac_n[1]=1;    for(int i=2;i<9;i++){        fac_n[i]=i*fac_n[i-1];    }}int Cantor(int a[]){    int ret=0;    for(int i=0;i<9;i++){        int cnt=0;        for(int j=i+1;j<9;j++){            if(a[i]>a[j])cnt++;        }        ret+=cnt*fac_n[9-i-1];    }    return ret;}bool bSolve(const Node &p){    int cnt=0;    for(int i=0;i<9;i++){        for(int j=i+1;j<9;j++){            if(p.s[i]>p.s[j]&&p.s[i]&&p.s[j])cnt++;        }    }    if(cnt&1)return false;    else return true;}int Get_H(const Node &p){    int ret=0;    for(int i=0;i<9;i++){        int t=p.s[i]-1;        int ri=t/3;        int ci=t%3;        ret+=abs(ri-i/3)+abs(ci-i%3);    }    return ret;}bool InMap(int ri,int ci){    return ri>=0&&ri<3&&ci>=0&&ci<3;}priority_queue<Node> SQ;pair<int,int> path[MAXN];void BFS(const Node &st,const Node &ed){    memset(vis,false,sizeof(vis));    memset(path,-1,sizeof(path));    while(!SQ.empty())SQ.pop();    SQ.push(st);    path[st.status]=make_pair(-1,-1);    vis[st.status]=true;    while(!SQ.empty()){        Node u=SQ.top();        SQ.pop();        if(u.status==ed.status)return ;        for(int di=0;di<4;di++){            Node v=u;            v.ri=u.ri+dir[di][0];            v.ci=u.ci+dir[di][1];            if(!InMap(v.ri,v.ci))continue;            int ti=3*v.ri+v.ci;            swap(v.s[ti],v.s[3*u.ri+u.ci]);            v.status=Cantor(v.s);            if(vis[v.status])continue;            vis[v.status]=true;            v.g=u.g+1;            v.h=Get_H(v);            v.f=v.g+v.h;            SQ.push(v);            path[v.status]=make_pair(u.status,di);            if(v.status==ed.status)return;        }    }}void Print(int si,int ed){    if(si==ed)return;    Print(path[si].first,ed);    putchar(d[path[si].second]);}int main(){    #ifdef L_JUDGE        freopen("in.txt","r",stdin);//      freopen("out.txt","w",stdout);    #endif    while(cin.getline(input,50,'\n')){        Init();        ed.ri=ed.ci=2;        for(int i=0;i<9;i++){            ed.s[i]=(i+1)%9;        }        ed.status=Cantor(ed.s);        int si=0;        for(int i=0;input[i];i++){            if(input[i]!=' '){                if(input[i]=='x'){                     st.s[si]=0;                     st.ri=si/3;                     st.ci=si%3;                }else{                    st.s[si]=input[i]-'0';                }                si++;            }        }        st.status=Cantor(st.s);        st.g=0;        st.h=Get_H(st);        st.f=st.h+st.g;        if(!bSolve(st)){            cout<<"unsolvable"<<endl;        }else{            BFS(st,ed);            int k=ed.status;            Print(ed.status,st.status);            printf("\n");        }    }    #ifdef L_JUDGE        fclose(stdin);        fclose(stdout);//      system("out.txt");    #endif    return 0;}

八数码问题变种解法 hdu3567

//重新编码初始状态和终点状态//前期预处理+打表#include<iostream>#include<cstdio>#include<cstring>//#include<vector>#include<algorithm>#include<queue>#define U_DEBUG#define L_JUDGE#ifdef L_JUDGE#pragma warning(disable:4996)#endifusing namespace std;const int MAXN=4e5+10;const int fac_n[10]={1,1,2,6,24,120,720,5040,40320,362880};const int dir[4][2]={1,0,0,-1,0,1,-1,0};int father[9][MAXN];char move1[9][MAXN];bool vis[9][MAXN];int Cantor(char s[]){    int ret=0;    for(int i=0;i<9;i++){        int cnt=0;        for(int j=i+1;j<9;j++){            if(s[i]>s[j])cnt++;        }        ret+=cnt*fac_n[9-i-1];    }    return ret;}struct Status{    char eight[9];    int space;    int status;    Status(){         memset(eight,0,sizeof(eight));         space=0;         status=0;    }    Status(char s[]){        for(int i=0;i<9;i++){             if(s[i]!='X'){                 eight[i]=s[i]-'0';             }             else {                 eight[i]=9;                 space=i;             }        }         status=Cantor(eight);    }}st,ed;int T,N;bool InMap(int ri,int ci){    return ri>=0&&ri<3&&ci>=0&&ci<3;}queue<Status> Q;void BFS(Status st,int pi){    while(!Q.empty())Q.pop();    memset(vis[pi],false,sizeof(vis[pi]));    memset(move1[pi],-1,sizeof(move1[pi]));    memset(father[pi],-1,sizeof(father[pi]));    Q.push(st);    vis[pi][st.status]=true;    move1[pi][st.status]=-1;    father[pi][st.status]=-1;    while(!Q.empty()){        Status u=Q.front();        Q.pop();        int k=u.space;        int x=k/3,y=k%3;        for(int di=0;di<4;di++){             int xx=x+dir[di][0];             int yy=y+dir[di][1];             if(!InMap(xx,yy))continue;             Status v=u;             v.space=3*xx+yy;             swap(v.eight[v.space],v.eight[k]);             v.status=Cantor(v.eight);             if(vis[pi][v.status])continue;             vis[pi][v.status]=true;             move1[pi][v.status]=di;             father[pi][v.status]=u.status;             Q.push(v);        }    }}int main(){    #ifdef L_JUDGE        freopen("in.txt","r",stdin);//      freopen("out.txt","w",stdout);    #endif    char str[20]="X12345678";    for(int i=0;i<9;i++){        st=Status(str);        BFS(st,i);        swap(str[i],str[i+1]);    }    scanf("%d",&T);    for(int ti=1;ti<=T;ti++){        scanf("%s",str);        st=Status(str);        int pi=0;        int mp[10],mpi=1;        for(int i=0;i<9;i++){            if(str[i]=='X'){                pi=i;            }else{                mp[str[i]-'0']=mpi++;            }        }        scanf("%s",str);        for(int i=0;i<9;i++){            if(str[i]!='X'){                str[i]=mp[str[i]-'0']+'0';            }        }        ed=Status(str);        char ans[1010];        int anslen=0;        int k=ed.status;        while(father[pi][k]!=-1){            switch(move1[pi][k]){                case 0:                    ans[anslen++]='d';                    break;                case 1:                    ans[anslen++]='l';                    break;                case 2:                    ans[anslen++]='r';                    break;                case 3:                    ans[anslen++]='u';                    break;            }             k=father[pi][k];        }        for(int i=0,j=anslen-1;i<j;i++,j--)            swap(ans[i],ans[j]);        ans[anslen]=0;        printf("Case %d: %d\n%s\n",ti,anslen,ans);    }    #ifdef L_JUDGE        fclose(stdin);        fclose(stdout);//      system("out.txt");    #endif    return 0;}