八数码问题
来源:互联网 发布:低头族数据统计 编辑:程序博客网 时间:2024/06/05 09:10
八数码问题:
编号为1~8的8个正方形滑块被摆成3行3列(有一个格子留空),每次可以把与空格相邻的滑块(又公共边才算相邻)移动到空格中,而他原来的位置就成了新的空格,给定初始局面和目标局面(0表示空格),你的任务时计算出最少移动的步数,无法达到输出-1.
样例输入:
2 6 4 1 3 7 0 5 88 1 5 7 3 6 4 0 2
样例输出:
31
分析:把八数码问题归结为图上的最短路径问题,图的“节点”就是9个格子中的滑块编号,(从上到下,从左到右把他们放到一个九个元素的数组中)
无权图上的最短路径问题可用BFS求解
还有就是重判的问题,如何重判呢?
第一种方法:把排列变成整数,然后只开一个一维数组,也就是说设计一套排列的编码和解码函数,把0~8的全排列和0~362879的整数意义一一对应起来。
时间效率高,但编码解码法适用范围并不大,如果隐士图总结点非常大,数组还是开不下。
int vis[362880], fact[9];void init_lookup_table(){ fact[0]=1; for(int i=1;i<9;i++) fact[i]=fact[i-1]*i;}int try_to_insert(int s){ int code=0; for(int i=0;i<9;i++){ int cnt=0; for(int j=i+1;j<9;j++) if(st[s][j]<st[s][i]) cnt++; code+= fact[8-i]*cnt; } if(vis[code]) return 0; return vis[code]=1;}第二种方法:把节点变成整数但不必是一一对应的,,换句话说,只需要设计一个所谓的哈希函数h(x),然后将任意节点的x映射到某个给定的范围[0,M-1]的整数即可,其中M是程序员根据可用内存大小自选的,在理想情况下,只需开一个大小为M的数组就能完成重判,但有时会有不同节点的哈希值相同,因此需要把哈希值相同的状态组织成链表。
const int hashsize = 1000003;int head[hashsize], next[maxstate];void init_lookup_table() {memset(head,0,sizeof(head));}int hash(State& s){ int v=0; for(int i =0;i<9;i++) v=v*10+s[i]; ///把9个数字组合成9位数 return v % hashsize; ///确保hash函数值是不超过hash表的大小的非负整数}int try_to_insert(int s){ int h=hash(st[s]); int u=head[h]; ///从表头开始查找链表 while(u) { if(memcmp(st[u],st[s],sizeof(st[s]))==0) return 0; ///找到了,插入失败 u = next[u]; } next[s]=head[h];///插入链表 head[h] = s; return 1;}需要注意:哈希表中对效率起作用的是哈希函数。
第三种:STL集合。
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]; if(vis.count(v)) return 0; vis.insert(v); return 1;}
下面贴上全部代码:
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<set>using namespace std;typedef int State[9];///定义状态类型const int maxstate=1000000;State st[maxstate], goal;///状态数组,所有状态都保存在这里int dist[maxstate];///距离数组///如果需要打印方案,可以在这里加一个“父亲编号”数组///int fa[maxstate]const int dx[]={-1,1,0,0};const int dy[]={0,0,-1,1};///BFS,返回目标状态在st数组下标///为了防止重判,但又不能开9维数组9的9次方是387420489///第一种方法:把排列编程整数,只开一个一维数组,设计一套编码解码///把0~8的全排列和0~362879的整数一一对应///第一种方法:编码解码法//int vis[362880], fact[9];//void init_lookup_table()//{// fact[0]=1;// for(int i=1;i<9;i++) fact[i]=fact[i-1]*i;//}//int try_to_insert(int s){// int code=0;// for(int i=0;i<9;i++){// int cnt=0;// for(int j=i+1;j<9;j++) if(st[s][j]<st[s][i]) cnt++;// code+= fact[8-i]*cnt;// }// if(vis[code]) return 0;// return vis[code]=1;//}///第二种方法:hash技术//const int hashsize = 1000003;//int head[hashsize], next[maxstate];//void init_lookup_table() {memset(head,0,sizeof(head));}//int hash(State& s){// int v=0;// for(int i =0;i<9;i++) v=v*10+s[i];// ///把9个数字组合成9位数// return v % hashsize;// ///确保hash函数值是不超过hash表的大小的非负整数//}//int try_to_insert(int s){// int h=hash(st[s]);// int u=head[h]; ///从表头开始查找链表// while(u)// {// if(memcmp(st[u],st[s],sizeof(st[s]))==0) return 0; ///找到了,插入失败// u = next[u];// }// next[s]=head[h];///插入链表// head[h] = s;// return 1;//}///第三种方法STL集合tset<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]; if(vis.count(v)) return 0; vis.insert(v); return 1;}int bfs(){ init_lookup_table(); ///初始化查找表 int front = 1, rear = 2;///不使用下标0,因为0被看作不存在 while(front < rear){ State& s=st[front];///引用简化代码 if(memcmp(goal,s,sizeof(s))==0) return front; ///memcmp是比较内存区域goal和s的前count个字节 ///找到目标状态,成功返回 int z; for(z=0;z<9;z++) if(!s[z]) break; ///找到0的位置 int x=z/3,y=z%3; ///获取行列编号(0~2) for(int d=0;d<4;d++){ int newx = x + dx[d]; int newy = y + dy[d]; int newz = newx * 3 + newy; if(newx >= 0&&newx < 3&&newy >= 0&&newy < 3) ///如果移动合法 { State& t = st[rear]; memcpy(&t,&s,sizeof(s)); ///扩展新节点 t[newz]=s[z]; t[z]=s[newz]; 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"); return 0;}
0 0
- 八数码问题
- [人工智能]八数码问题
- 八数码问题.....
- 八数码问题
- 八数码问题分析
- 八数码问题
- 人工智能-八数码问题
- 八数码问题
- 八数码问题,Puzzle
- 八数码问题
- 八数码问题
- 八数码问题
- 八数码问题源代码
- 八数码问题
- 八数码问题
- 八数码问题
- 八数码问题
- 八数码问题
- 四月份遇到的一些笔试题(回忆版一)
- Windows下Python安装lxml
- ERROR 1146 (42S02): Table 'mysql.slow_log' doesn't exist
- 获取inflater 的三种方式
- 使用wrapper将java程序安装成windows服务(摘)
- 八数码问题
- C++ 初始化列表
- sdcc man阅读笔记(二)——基础编译选项&output files
- CentOS上安装GitLab
- runnable和thread的区别以及run和start的区别(多线程)
- 自定义eclipse一行显示字符串的数目
- MS Server, MySql,Oracle数据库的区别
- C# 文件正由另一进程使用,该进程无法访问该文件
- eclipse设置字体大小