八数码问题
题意:
编号为1~8的8个正方形滑块被摆成3行3列(有一个格子留空),如下图所示 每次可以把与空格相邻的滑块(有公共边才算相邻)移到空格中,而他原来的位置 就成为了新的空格。如果无法到达目标局面,则输出-1 。
2 | 6 | 4 |
1 | 3 | 7 |
5 | 8 |
8 | 1 | 5 |
7 | 3 | 6 |
4 | 2 |
样例输入:
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
样例输出:
31
【分析】
八数码很像我们小时候玩的拼图游戏,就是把打乱的图拼成指定形状。可以 把题看成求最短路径问题,只是在原来的基础上增加了不少难度,增加了每步的 判断0的位置,和0的移动,还要保存每步后的移动结果,判断重复。数组要开大
因为数据移动最大可达到9!个(9!是多少,啊我忘了,反正几十万),还要定 义长度为9的数组,要来保存移动时的数据情况。
【代码】
<span style="background-color: rgb(51, 255, 255);">#include<stdio.h> #include<string.h> #include<set> using namespace std; typedef int State[9]; // 储存表格 const int maxstate = 1000000; // 最大1000000步 State st[maxstate],goal; // 定义goal[9],定义st[maxstate][9] int dist[maxstate]; // 存储步数 const int dx[]={-1,1,0,0}; const int dy[]={0,0,-1,1}; set<int>vis; void init_lookup_table() { vis.clear(); } int try_to_insert(int s) { int v=0; for(int i=0;i<9;i++) v=v*10+st[s][i]; // 将现在的阵列生成一个9位数 if(vis.count(v)) // 查找是否用过 return 0; vis.insert(v); // 插入set容器,用于标记 return 1; } // 广搜 int bfs() { init_lookup_table(); int front=1,rear=2; while(front<rear) { State& s=st[front]; // 将现在的位置保存到s中 if(memcmp(goal,s,sizeof(s))==0) // 判断与目标是否相等 return front; int z; for(z=0;z<9;z++) if(!s[z]) // 查找0的位置 break; int x=z/3,y=z%3; // 0的x,y坐标 for(int d=0;d<4;d++) { int newx=x+dx[d]; // 0可以的的点 int newy=y+dy[d]; int newz=newx*3+newy; // 0可移动到点(newx,newy)处,newz表示在串的第几个 //printf("newz=%d,z=%d\n",newz,z); if(newx>=0&&newx<3&&newy>=0&&newy<3) // 判断是否越界 { State& t=st[rear]; // 定义t[9] memcpy(&t,&s,sizeof(s)); // 将s的数据赋给t // 0的移动,两位置互换 t[newz]=s[z]; // 移动0到(newx,newy)处 t[z]=s[newz]; // (newx,newy)的数据移动原来0的位置 // printf("t[newz]=%d t[z]=%d,z=%d\n",t[newz],t[z],z); dist[rear]=dist[front]+1; // 储存步数 if(try_to_insert(rear)) rear++; // 尾指针后移 } } front++; } return 0; } int main() { for(int i=0;i<9;i++) scanf("%d",&st[1][i]); for(int i=0;i<9;i++) scanf("%d",&goal[i]); int ans=bfs(); // 返回头地址 if(ans>0) printf("%d\n",dist[ans]); // 输出存储的步数 else printf("-1\n"); // 如果找不到输出-1 return 0; }</span>代码注释有不当之处或有更好的代码,麻烦大神留言!