ACM之八数码问题BFS搜索数独游戏的模拟(上)

题目描述;数独游戏的内核模拟
八数码问题;
编号为1到8的8个正方形滑块被摆成3行3列;(有一个格子留空);
每次可以把与空格相邻的滑块(有公共边才算相邻)移到空格中;
而它原来的位置就成为了新的空格;给定初始局面和目标局面(用0表示空格);
你的任务死计算出最少的移动步数;和移动过程;如果无法到达目标局面,则输出-1;

2 6 4
1 3 7
  5 8
 2
 3  6
 4    2

 这个问题也是要分步骤实现的,自然也涉及到状态的问题,和我的前一篇博客一样,使用的是广度优先遍历

这样可以找到最少的步数;众所周知的是BFS算法之于树和之于图是不太一样的,

因为树一旦往下走便不必担心访问到已经访问过的结点,而图呢由于有环的存在,所以会回到之前访问过的状态;

这样就需要我们写一个判断是否重复的方法,上一篇博客里面的情况比较简单,因为可以直接用状态做下标进行随机访问;

这里的情况就不一样了;我们先用c语言的遍历来实现一下,代码如下;

使用头文件string.h因为这里要用到memcmp和memcpy函数和memset;
这两个函数比我们写的循环效率要高

为什么数组选择这么大呢?排列数9!;

这里我把所有的变量都声明成全局变量的,在c语言编程方面不是一个好的编程style;
但是在ACM这种对数据量要求极大的,情况下,数组申请在全局变量比在栈中申请要好;
另外也方便了函数之间数据的交互,不需要多写什么参数了,

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h
  4 #define MAX 1000000
  5 typedef struct Node
  6 {
  7     int state[9];//存放本状态各个位置的数码值;
  8     int fa;//记录父节点的下标;
  9     int deepth;
 10     int last_x;
 11     int last_y;
 12 
 13 }Node;
 14 Node q[MAX];//构成状态数组;
 15 typedef struct Vis
 16 {
 17     int sequeue[9];//记录9个数码的位置;
 18     int visited;
 19 
 20 }Vis;

 21 Vis vis[MAX];//判断是否访问过;
 22 int vis_cur=0;//记录vis数组的当前索引位置;
 23 const int dx[4]={-1,1,0,0};//左右上下;
 24 const int dy[4]={0,0,-1,1};
 25 //bool has_vis(const int * state_to_decide);
 26 int has_vis=0;
 27 int bfs();//广度优先找到目标状态;
 28 void print_path(int founded);//根据fa成员,通过递归技术实现状态依次输出;
 29 Node destination;//存储目标状态;
 30 int i,j,k;
 31 int main()
 32 {
 33     /*首先输入起始状态和目标状态*/
 34     memset(q,0,sizeof q);
 35     for(i=0;i<9;i++)
 36     {
 37         scanf("%d",&(q[0].state[i]));
 38     }
 39     for(i=0;i<9;i++)
 40     {
 41         scanf("%d",&destination.state[i]);
 42     }
 43         memset(vis,0,sizeof vis);
 44     
 45     /*然后进行搜索并输出*/
 46     int founded=bfs();
 47     if(founded)
 48         print_path(founded);
 49     else
 50         printf("-1\n");
 51     system("pause");
 52     return 0;
 53 }
 54 int bfs()
 55 {
 56     memcpy(vis[0].sequeue,q[0].state,sizeof q[0].state);
 57     int front=0,rear=1;//用来模拟队列的先进先出,达到广度优先的目的;
 58     vis[0].visited=1;//第一个结点访问;
 59     vis_cur++;
 60     while (front<rear)
 61     {
 62         Node &first=q[front];
 63         if (memcmp(first.state,destination.state,sizeof destination.state)==0)
 64         {//找到了目标状态;
 65             return front;
 66         }
 67         for(i=0;i<9;i++)
 68             if (!first.state[i])
 69             {//找到空格处;
 70                 break;
 71             }
 72 
 73         for(j=0;j<4;j++)
 74         {//向四个方向进行转换;
 75             Node &new_Node=q[rear];
 76             memcpy(new_Node.state,first.state,sizeof first.state);
 77             int new_x=i%3+1+dx[j];
 78             int new_y=i/3+1+dy[j];
 79             
 80             if (new_x>0&&new_y>0&&new_x<4&&new_y<4)
 81             {
 82                 //位置合法
 83                 new_Node.state[i]=new_Node.state[i+dx[j]+3*dy[j]];//空格位置;
 84                 new_Node.state[i+dx[j]+3*dy[j]]=0;//新的状态形成了;
 85                 has_vis=0;
 86                 for(k=0;k<vis_cur;k++)//这里不用i,j因为i,j是全局变量且本函数是在循环里面的;
 87                 {
 88                     if((memcmp(vis[k].sequeue,new_Node.state,sizeof vis[k].sequeue))==0)
 89                     {
 90                         has_vis=1;
 91                         break;
 92                     }
 93                 }
 94                 if(!has_vis)//没有被访问;
 95                 {
 96                     new_Node.fa=front;
 97                     new_Node.deepth=first.deepth+1;
 98                     new_Node.last_x=dx[j];
 99                     new_Node.last_y=dy[j];
100                     memcpy(vis[vis_cur].sequeue,new_Node.state,sizeof new_Node.state);
101                     vis[vis_cur].visited=1;
102                     vis_cur++;
103                     rear++;
104                 }//if
105                 
106             }//if
107         }//for
108         front++;
109         printf("%d %d\n",front,q[front].deepth);
110     }//while
111     return 0;
112 }
113 void print_path(int founded)
114 {
115     if(q[founded].fa!=founded)
116     {
117         print_path(q[founded].fa);
118     }
119     for(i=0;i<3;i++)
120     {
121         for(j=0;j<3;j++)
122         {
123             printf("%d ",q[founded].state[3*i+j]);
124         }
125         printf("\n");
126     }
127     printf("\n");
128 }
129 /*bool has_vis(const int * state_to_decide)
130 {
131     for(k=0;k<vis_cur;k++)//这里不用i,j因为i,j是全局变量且本函数是在循环里面的;
132     {
133         if(memcmp(vis[k].sequeue,state_to_decide,sizeof vis[k].sequeue)==0)
134             return true;
135     }
136     return false;
137 }*/

最后如你所见,我们的这个程序读取输入之后似乎没什么反应,其实它还是在工作的,我也是等了30多分钟之后才看到了曙光;

显然这种遍历判断是否访问过的想法不仅没有提高效率,反而增加了上万次的无辜操作;

那么该怎么提高效率呢,在下一个博客里面,我会用c++的stl库试一下;

原文地址:https://www.cnblogs.com/dragonfive/p/2979529.html