关于八数码问题中的状态判重的三种解决方法(编码、hash、<set>)
来源:互联网 发布:spss 24 mac 授权码 编辑:程序博客网 时间:2024/05/22 12:22
八数码问题搜索有很多高效方法:如A*算法、双向广搜等
但在搜索过程中都会遇到同一个问题,那就是判重操作(如果重复就剪枝),如何高效的判重是8数码问题中效率的关键
下面关于几种判重方法进行比较:编码、hash、set
看到问题初学者最先想到的应该就是用一个vis数组标志一下即可。但是该申请多大的数组呢?一个9维数组(9^9=387420489太大了吧)?如果内存允许这是最高效的办法:O(1)
所以我们现在面临的问题是如何在O(1)的时间复杂度不变的情况下把空间压缩下来:
方法一:编码、解码,我们可以发现8数码问题最多有9!=362880个状态,如果我们对这些状态进行编码,用一个362880大小的数组就可以了,内存消耗大大降低,效率也基本不变,效率很高。但对于问题中状态过多时这种方法存在局限性。
代码:
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函数:效率很高,这种方法是用范围比较广。hash函数的选取很重要(好的hash函数冲突小)。前面的编码相当于一种完美的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]; return v%hashsize;}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集合:编码相对简单了许多,但是这种方法效率也最低,对与时间要求比较高的题目,我们可以先用set,然后用hash代替
代码:
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;}
题目链接:点击打开链接
通过题目看效率:vijos八数码问题
编码:122msAC
hash:197msAC
set:932msAC
代码:
#include <cstdio>#include <cstring>#include <iostream>#include <set>typedef int State[9];using namespace std;const int maxstate=1000000;State st[maxstate],goal={1,2,3,8,0,4,7,6,5};int dist[maxstate];int fa[maxstate];const int dx[]={-1,1,0,0};const int dy[]={0,0,-1,1};/********************编码、解码***********************/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]; return v%hashsize;}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集合************************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;}***********************************************/int bfs(){ init_lookup_table(); int front=1,rear=2; while(front<rear){ State &s=st[front]; if(memcmp(goal,s,sizeof(s))==0) return front; int z; for(z=0;z<9;++z) if(!s[z]) break; int x=z/3,y=z%3; 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; fa[rear]=front; if(try_to_insert(rear)) rear++; } } front++; } return 0;}int main(){ char ch; for(int i=0;i<9;i++) { //scanf("%d",&st[1][i]); cin>>ch; st[1][i]=ch-'0'; } //for(int i=0;i<9;i++) scanf("%d",&goal[i]); fa[1]=-1; int ans=bfs(); if(ans>0) printf("%d\n",dist[ans]); else printf("-1\n"); return 0;}
1 0
- 关于八数码问题中的状态判重的三种解决方法(编码、hash、<set>)
- 基于八数码问题的hash判重
- 八数码问题(bfs,hash判重)
- 【BFS】由八数码问题的BFS解法引出的三种BFS经典状态判重方法
- 八数码问题和bfs中的判重方法
- 八数码中的hash问题
- pku 1077 Eight【八数码、广搜、hash判重】
- hdoj--1043--八数码--bfs||A*(HASH判重--康托)
- HDU 1043 Eight ((八数码问题)逆向BFS + 康托定理判重)
- 八数码问题(hash+bfs)
- 八数码问题<set的使用>bfs
- hdu1043(八数码问题,广搜 + hash(实现状态压缩) )
- 八数码问题 BFS+hash
- (隐式图)八数码问题(三种判重方法:排列计数、哈希技术、STL_set判重)
- 八数码问题的三种算法解答(C#源代码)
- 八数码问题的三种算法解答(C#源代码) (补充)
- 八数码问题,bfs,hash,康托
- 八数码的八重境界
- 并查集(转)
- layoutSubViews调用时机(转载的基础上添加一些自己的总结)
- web之HTML详解
- js与jquery获取滚动条距浏览器顶部的距离
- [leetcode-100]Same Tree(C)
- 关于八数码问题中的状态判重的三种解决方法(编码、hash、<set>)
- 20-07-其他对象API(Date类-练习)
- Codeforces Round #315 div2 B-Inventory 标记,水题
- Ubuntu14.04重新安装图形界面
- throws 和 try catch 的区别与联系
- struts2常用的Constant总结
- hdoj 1233 还是畅通工程【最小生成树 kruskal && prim】
- [IOS]mac远程window全屏显示
- android获取设备屏幕大小的方法