【luogu1379】 八数码难题

博客探讨了八数码难题的解决方法,包括从初始状态转换到目标状态所需的最少移动次数。介绍了使用单向广度优先搜索(BFS)可能会导致超时,提出通过简化版BFS或结合哈希表来优化。还提到了其他解题策略如A*和IDA*,并建议使用STL中的map或set进行优化。文章提供了实现算法的关键步骤,包括棋盘存储、节点定义、移动方式等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(http://www.elijahqi.win/2017/07/05/%E3%80%90luogu1379%E3%80%91-%E5%85%AB%E6%95%B0%E7%A0%81%E9%9A%BE%E9%A2%98/)
题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入输出格式
输入格式:

输入初试状态,一行九个数字,空格用0表示

输出格式:

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

输入输出样例
输入样例#1:
283104765
输出样例#1:
4

题解:直接单向bfs会导致超时

可以考虑单向bfs为简化版 求 某一状态到另一状态的最少步数

棋盘的位置我们使用数组存储为二维

1.定义棋盘
int a[4][4]
2.定义节点
(1) 棋盘
(2)空位坐标
(3)父节点

struct node{
   int a[4][4];
   int x,y;
   int fa;
};

3.定义队列(一维数组,存储每一个节点)
队列里头尾指针

node data[33000]
int op=0;int cl=1;

4.空位的移动方式
定义上、下、左、右偏移量

struct node1{
   int x,y; 
};
node1[5];
d[1].x=-1;d[1].y= 0;
d[2].x= 1;d[2].y= 0;
d[3].x= 0;d[3].y=-1;
d[4].x= 4;d[4].y= 1;

5.same(a,b)判断a棋盘和b棋盘是否相同

bool same(int a[4][4],int b[4][4]){
   for(int i=1;i<=3;++i) for(int j=1;j<=3;++j) if(a[i][j]!=b[i][j]) return false;
   return true;
}

6.设计函数exist(a)判断a棋盘是否在data队列中从1~cl

bool exist(int a[4][4]){
   for(int i=1;i<=cl;++i) if(same(data[i].a,a)) return true;
   return false;
}

7.设置函数实现a复制到b

void copy1(int a[4][4],int b[4][4]){
   for(int i=1;i<=3;++i) for(int j=1;j<=3;++j) b[i][j]=a[i][j];
}

8.宽搜的框架

(1)将初始节点存入data[1]中

(2)

while(op<cl){
   ++op;
   取出op对应的节点的棋盘存入a1
   取出op对应的节点的空位位置 x1,y1
   对棋盘a1[x1][y1] 处理上下左右生成4个新的棋盘
   for(int i=1;i<=4;++i){
    利用x1 y1 生成x2 y2
    x2=x1+d[i].x;y2=x2+d[i].y;
    判断x2 y2 是否合法
    if(x2<=3 and x1>=1 and y1>=1 and y2<=3){
       利用a1生成a2
       copy1(a1,a2);
       a2[x1][y1]=a2[x2][y2];
       a2[x2][y2]=0;
       判断a2是否在data
       if(!exist(a2)){
        ++cl;
        a2存入data数组中的cl位置上
        copy1(a2,data[cl].a);
        data[cl].x=x2;
        data[c1].y=y2;
        data[cl].fa=op;
        判断a2是否是目标
        if(same(a2,f1)){
           输出
        }
       }
    }

   }
return 0;
}

9.输出函数

void printf(){
  int t=0;
   //计数器记录方案的每一步
   while(cl>0){
      ans[++t]=cl;
      cl=data[cl].fa;
   }
   for(int i=t;i>=1;--i) printf("%d",ans[i]);
   输出ans[i] 位置的棋盘
   print1(data[ans[i]].a);
}
void print1(int a[4][4]){
    for(int i=1;i<=3;++i){
    for(int j=1;j<=3;++i) printf("%d",a[i][j]);
      print("\n");
   }
}

正解:hash+单向bfs 如果比较省事可以考试stl中的map或者set 我尝试过 只有在大牛分站开o2优化(大牛分站只要提交全部都o2优化)是可以过的

看到其他blog中有使用A*或者IDA* 膜一下ORZ 还没学

a数组用来统计0从右到左第一次出现的位置 q 表示这个bfs队列

提早初始化p数组 用来储存10^x 方便取位计算

solve函数表示 solve(x,y)表示 从y的位置移动到x的位置

#include <cstdio>
#include <cstring> 
#define terminal 123804765
#define N 402880  //362880=9!
#define M 1999993
#define N1 2200000
int q[N],now,nows,op,cl,d[N],p[20],a[N],st;
struct node{
    int head[N1];
    int size;
    int state[N];
    int next[N];
    void init(){
        size=0;memset(head,-1,sizeof(head));
    }
    inline void insert(int x){
        int h=x%M;
        state[size]=x;next[size]=head[h];head[h]=size++;
    }
    inline bool find(int x){
        int h=x%M;
        for (int i=head[h];i!=-1;i=next[i]){
            if (state[i]==x) return true;
        }
        return false;
    } 
}hash;
bool solve(int i,int j){
    int tmp=nows-nows/p[j]%10*p[j]+nows/p[j]%10*p[i];
    //printf("%d\n",tmp);
    if (!hash.find(tmp)){
        if (tmp==terminal){
            printf("%d",++d[op]);return true;
        }
        hash.insert(tmp);
        a[++cl]=j;
        q[cl]=tmp;
        d[cl]=d[op]+1;
    }
    return false;
} 
void init(){
    p[1]=1;
    for (int i=2;i<=10;++i) p[i]=p[i-1]*10;
} 
int main(){
    freopen("1379.in","r",stdin);
    freopen("1379.out","w",stdout);
    scanf("%d",&st);
    init();q[1]=st;
    for (int i=1;i<=9;++i){
        if (st%10==0){a[1]=i;break;}
        st/=10;
    }
    op=0,cl=1;
    hash.init();
    while (op<cl){
        now=a[++op],nows=q[op];
        if (now%3) {bool ff=solve(now,now+1); if (ff) return 0; }
        if ((now-1)%3) {bool ff=solve(now,now-1); if (ff) return 0; };
        if (now<7) {bool ff=solve(now,now+3); if (ff) return 0; };
        if (now>3) {bool ff=solve(now,now-3); if (ff) return 0; };
    } 
    return 0;
}

bfs+set

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
string start,end="123804765";
set <string> v;//某状态是否被访问 
string q[1000001],fx[9]={"13","024","15","046","1357","248","37","468","57"};
int ql,qr,t[1000001];
void bfs(){
    while (qr>ql){
        string s=q[ql]; int temp=t[ql],begin=s.find("0",0); ql++;
        for (int i=0;i<fx[begin].size();i++){
            int next=fx[begin][i]-48;string ss=s;
            char tt=ss[begin]; ss[begin]=ss[next]; ss[next]=tt;
            if (ss==end) {printf("%d",temp+1); return;}
            if (v.count(ss)==0){//count 是返回元素个数,因为set里一个元素只出现一次,所以可一理解成count=1时,该情况已被访问 
                q[qr]=ss;t[qr]=temp+1;qr++;v.insert(ss);
            } 
        }
    }
    return;
}
int main(){
    cin>>start;
    if (start==end) printf("0");
    q[qr]=start;t[qr]=0;qr++;v.insert(start);
    bfs();
    return 0;
}

双向bfs

#include <cstdio>
#include <cstring>
int const N=362881;
int x1,y1,x2,y2,k,cl1,cl2,x,y,t,h,g,z,p;
int st[5][5],fi[5][5],a[5][5],f[110000];
struct node1{
    int a[5][5];
    int x,y;
    int dep;
    int ch;
    int fa;
};
node1 data1[N],data2[N];

struct node2{
    int s,t;
};
node2 h1[110],h2[110];

struct node3{
    int x,y;
};
node3 d[5];

inline bool same(int a[5][5],int b[5][5]){
    for(int i=1;i<=4;++i)
        for(int j=1;j<=4;++j)
            if(a[i][j]!=b[i][j]) return false;

    return true;
}
inline void copy(int a[5][5],int b[5][5]){
    for(int i=1;i<=4;++i)
        for(int j=1;j<=4;++j)
            b[i][j]=a[i][j];    

    return ;
}
inline bool exist11(int a[5][5],int k){
    int z=1;
    if(k<3) z=1;
    else z=h1[k-2].s;

    for(int i=z;i<=h1[k].t;++i){
        if(same(a,data1[i].a)) return true;
    }

    return false;
}
inline bool exist21(int a[5][5],int k){
    int z=1;
    if(k<3) z=1;
    else z=h2[k-2].s;

    for(int i=z;i<=h2[k].t;++i){
        if(same(a,data2[i].a)) return true;
    }

    return false;
}
inline int exist12(int a[5][5],int k){
    for(int i=h2[k].s;i<=h2[k].t;++i)
        if(same(a,data2[i].a)) return i;

    return 0;
}
inline int exist22(int a[5][5],int k){
    for(int i=h1[k].s;i<=h1[k].t;++i)
        if(same(a,data1[i].a)) return i;

    return 0;
}
void print(){
    int ans1[110000];
    int t1=0;
    int p1=data1[cl1].fa;
    while(p1>0){
        ans1[++t1]=data1[p1].ch;
        p1=data1[p1].fa;
    }
    for(int i=t1-1;i>=1;--i){
        if(ans1[i]==1) printf("u");
        if(ans1[i]==2) printf("d");
        if(ans1[i]==3) printf("l");
        if(ans1[i]==4) printf("r");
    }


    int ans2[110000];
    int t2=0;
    int p2=data2[cl2].fa;
    while(p2>0){
        ans2[++t2]=data2[p2].ch;
        p2=data2[p2].fa;
    }
    for(int i=t2-1;i>=1;--i){
        if(ans2[i]==1) printf("d");
        if(ans2[i]==2) printf("u");
        if(ans2[i]==3) printf("r");
        if(ans2[i]==4) printf("l");
    }

    return ;
}
int main(){
//    freopen("ex11.in","r",stdin);
    for(int i=1;i<=3;++i)
        for(int j=1;j<=3;++j){
            scanf("%c",&st[i][j]);
            if(st[i][j]!='0'){
                st[i][j]-='0';
            }
            if(st[i][j]=='0'){
                st[i][j]=0;
                x1=i;y1=j;
            }    
        }
    /*for(int i=1;i<=3;++i){
        for(int j=1;j<=3;++j){
            printf("%d ",st[i][j]);
        }
        printf("\n");
    }*/


    fi[1][1]=1;fi[1][2]=2;fi[1][3]=3;
    fi[2][1]=8;fi[2][2]=0;fi[2][3]=4;
    fi[3][1]=7;fi[3][2]=6;fi[3][3]=5;    
    x2=2;y2=2;

    if(same(st,fi)){
        printf("0");
        return 0;
    }

    d[1].x=-1;d[1].y= 0;
    d[2].x= 1;d[2].y= 0;
    d[3].x= 0;d[3].y=-1;
    d[4].x= 0;d[4].y= 1;

    copy(st,data1[1].a);
    data1[1].dep=0;
    data1[1].fa=0;
    data1[1].ch=0;
    data1[1].x=x1;data1[1].y=y1;
    cl1=1;
    h1[1].s=1;h1[1].t=1;

    copy(fi,data2[1].a);
    data2[1].dep=0;
    data2[1].fa=0;
    data2[1].ch=0;
    data2[1].x=x2;data2[1].y=y2;
    cl2=1;
    h2[1].s=1;h2[1].t=1;

    k=1;
    while(h1[k].s<=h1[k].t and h2[k].s<=h2[k].t){


        h1[k+1].s=h1[k].t+1;

        for(int op=h1[k].s;op<=h1[k].t;++op){
            for(int i=1;i<=4;++i){
                x=data1[op].x+d[i].x;y=data1[op].y+d[i].y;
                if(x>0 and x<4 and y>0 and y<4){
                    copy(data1[op].a,a);
                    a[data1[op].x][data1[op].y]=a[x][y];
                    a[x][y]=0;
                    if(!exist11(a,k)){
                        ++cl1;
                        copy(a,data1[cl1].a);
                        data1[cl1].dep=data1[op].dep+1;
                        data1[cl1].ch=i;
                        data1[cl1].fa=op;
                        data1[cl1].x=x;data1[cl1].y=y;

                        t=exist12(a,k);
                        if(t){
                            printf("%d\n",data1[cl1].dep+data2[t].dep);
                            //print();
                            /*h=cl1;
                            while (data1[h].fa>0){
                              ++g;
                              f[g]=data1[h].ch;
                              h=data1[h].fa;
                            }
                            for (int j=g;j>=1;--j){
                                if (f[j]==1) printf("u");
                                if (f[j]==2) printf("d");
                                if (f[j]==3) printf("l");
                                if (f[j]==4) printf("r");
                                //printf("%c",w[f[j]]);
                            }
                            h=t;
                            while (data2[h].fa>0){
                                if (data2[h].ch==1) printf("d");
                                if (data2[h].ch==2) printf("u");
                                if (data2[h].ch==3) printf("r");
                                if (data2[h].ch==4) printf("l");
                                //printf("%c",w[5-data2[h].ch]);
                                h=data2[h].fa;
                            }*/
                            return 0;
                        }
                    }
                }
            }
        }
        h1[k+1].t=cl1;






        h2[k+1].s=h2[k].t+1;

        for(int op=h2[k].s;op<=h2[k].t;++op){
            for(int i=1;i<=4;++i){
                x=data2[op].x+d[i].x;y=data2[op].y+d[i].y;
                if(x>0 and x<4 and y>0 and y<4){
                    copy(data2[op].a,a);
                    a[data2[op].x][data2[op].y]=a[x][y];
                    a[x][y]=0;
                    if(!exist21(a,k)){
                        ++cl2;
                        copy(a,data2[cl2].a);
                        data2[cl2].dep=data2[op].dep+1;
                        data2[cl2].ch=i;
                        data2[cl2].fa=op;
                        data2[cl2].x=x;data2[cl2].y=y;

                        t=exist22(a,k+1);
                        if(t){
                            printf("%d\n",data2[cl2].dep+data1[t].dep);
                            //print();
                            /*h=cl1;
                            while (data1[h].fa>0){
                              ++g;
                              f[g]=data1[h].ch;
                              h=data1[h].fa;
                            }
                            for (int j=g;j>=1;--j){
                                if (f[j]==1) printf("u");
                                if (f[j]==2) printf("d");
                                if (f[j]==3) printf("l");
                                if (f[j]==4) printf("r");

                            }
                            h=t;
                            while (data2[h].fa>0){
                                if (data2[h].ch==1) printf("d");
                                if (data2[h].ch==2) printf("u");
                                if (data2[h].ch==3) printf("r");
                                if (data2[h].ch==4) printf("l");

                                h=data2[h].fa;
                            }*/
                            return 0;
                        }
                    }
                }
            }
        }
        h2[k+1].t=cl2;
        k+=1; 
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值