您的位置:首页 > 其它

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

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

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

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

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

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

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

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

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h
#define MAX 1000000
typedef struct Node
{
int state[9];//存放本状态各个位置的数码值;
int fa;//记录父节点的下标;
int deepth;
int last_x;
int last_y;

}Node;
Node q[MAX];//构成状态数组;
typedef struct Vis
{
int sequeue[9];//记录9个数码的位置;
int visited;

}Vis;

Vis vis[MAX];//判断是否访问过;
int vis_cur=0;//记录vis数组的当前索引位置;
const int dx[4]={-1,1,0,0};//左右上下;
const int dy[4]={0,0,-1,1};
//bool has_vis(const int * state_to_decide);
int has_vis=0;
int bfs();//广度优先找到目标状态;
void print_path(int founded);//根据fa成员,通过递归技术实现状态依次输出;
Node destination;//存储目标状态;
int i,j,k;
int main()
{
/*首先输入起始状态和目标状态*/
memset(q,0,sizeof q);
for(i=0;i<9;i++)
{
scanf("%d",&(q[0].state[i]));
}
for(i=0;i<9;i++)
{
scanf("%d",&destination.state[i]);
}
memset(vis,0,sizeof vis);

/*然后进行搜索并输出*/
int founded=bfs();
if(founded)
print_path(founded);
else
printf("-1\n");
system("pause");
return 0;
}
int bfs()
{
memcpy(vis[0].sequeue,q[0].state,sizeof q[0].state);
int front=0,rear=1;//用来模拟队列的先进先出,达到广度优先的目的;
vis[0].visited=1;//第一个结点访问;
vis_cur++;
while (front<rear)
{
Node &first=q[front];
if (memcmp(first.state,destination.state,sizeof destination.state)==0)
{//找到了目标状态;
return front;
}
for(i=0;i<9;i++)
if (!first.state[i])
{//找到空格处;
break;
}

for(j=0;j<4;j++)
{//向四个方向进行转换;
Node &new_Node=q[rear];
memcpy(new_Node.state,first.state,sizeof first.state);
int new_x=i%3+1+dx[j];
int new_y=i/3+1+dy[j];

if (new_x>0&&new_y>0&&new_x<4&&new_y<4)
{
//位置合法
new_Node.state[i]=new_Node.state[i+dx[j]+3*dy[j]];//空格位置;
new_Node.state[i+dx[j]+3*dy[j]]=0;//新的状态形成了;
has_vis=0;
for(k=0;k<vis_cur;k++)//这里不用i,j因为i,j是全局变量且本函数是在循环里面的;
{
if((memcmp(vis[k].sequeue,new_Node.state,sizeof vis[k].sequeue))==0)
{
has_vis=1;
break;
}
}
if(!has_vis)//没有被访问;
{
new_Node.fa=front;
new_Node.deepth=first.deepth+1;
new_Node.last_x=dx[j];
new_Node.last_y=dy[j];
memcpy(vis[vis_cur].sequeue,new_Node.state,sizeof new_Node.state);
vis[vis_cur].visited=1;
vis_cur++;
rear++;
}//if

}//if
}//for
front++;
printf("%d %d\n",front,q[front].deepth);
}//while
return 0;
}
void print_path(int founded)
{
if(q[founded].fa!=founded)
{
print_path(q[founded].fa);
}
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",q[founded].state[3*i+j]);
}
printf("\n");
}
printf("\n");
}
/*bool has_vis(const int * state_to_decide)
{
for(k=0;k<vis_cur;k++)//这里不用i,j因为i,j是全局变量且本函数是在循环里面的;
{
if(memcmp(vis[k].sequeue,state_to_decide,sizeof vis[k].sequeue)==0)
return true;
}
return false;
}*/


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

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

那么该怎么提高效率呢,在下一个博客里面,我会用c++的stl库试一下;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: