关于八数码问题中的状态判重的三种解决方法(编码、hash、<set>)
八数码问题搜索有非常多高效方法:如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;
}
关于八数码问题中的状态判重的三种解决方法(编码、hash、<set>)的更多相关文章
- fluent中UDF环境变量问题的三种解决方法
方法一: 这种方式最简便,首选这种,但是有时会因为不明原因而不好使,我自己电脑刚开始用这种方式是行得通的,但是后来中途装过很多乱七八糟的软件,估计环境变量改乱了,这时候只能用第二种或者第三种方法.先说 ...
- [转]PHP开发中涉及到emoji表情的三种处理方法
最近几个月做微信开发比较多,存储微信昵称必不可少,可这万恶的微信支持emoji表情做昵称,这就有点蛋疼了 一般Mysql表设计时,都是用UTF8字符集的.把带有emoji的昵称字段往里面insert一 ...
- PHP中出现Notice: Undefined index的三种解决办法
前一段做的一个PHP程序在服务器运行正常,被别人拿到本机测试的时候总是出现“Notice: Undefined index:”这样的警告,这只是一个因为PHP版本不同而产生的警告(NOTICE或者WA ...
- Springboot中关于跨域问题的一种解决方法
前后端分离开发中,跨域问题是很常见的一种问题.本文主要是解决 springboot 项目跨域访问的一种方法,其他 javaweb 项目也可参考. 1.首先要了解什么是跨域 由于前后端分离开发中前端页面 ...
- mysqli:查询数据库中,是否存在数据的三种校验方法
在我们编辑用户登录功能的时候,常常需要对用户输入的信息进行校验,校验的方法就是通过SQL语句进行一个比对,那么我们就需要用到以下三种中的一种进行校验啦 1.使用mysqli_num_rows()校验 ...
- .NET中TextBox控件设置ReadOnly=true后台取不到值三种解决方法
当TextBox设置了ReadOnly=true后要是在前台为控件添加了值,后台是取不到的,值为空,多么郁闷的一个问题经过尝试,发现可以通过如下的方式解决这个问题.感兴趣的朋友可以了解下 当TextB ...
- .NET中TextBox控件设置ReadOnly=true后台取不到值 三种解决方法
方法一:不设置ReadOnly属性,通过onfocus=this.blur()来模拟,如下: <asp:TextBox ID="TextBox1" runat="s ...
- Electron与jQuery中$符号冲突的三种解决方法
在Electron工程中引用jQuery时,经常会出现以下错误: Uncaught ReferenceError: $ is not defined 解决的具体方法如下: ①.在测试的过程中(测试过1 ...
- listener中@Autowired无法注入bean的一种解决方法
背景:使用监听器处理业务,需要使用自己的service方法: 错误:使用@Autowired注入service对象,最终得到的为null: 原因:listener.fitter都不是Spring容器管 ...
随机推荐
- c中结构体的4种定义
1.常规的标准方式: 1 #include <stdio.h> 2 3 struct student{ 4 int age; 5 float score; 6 ...
- css之其它技巧和经验列表
其它技巧和经验列表(*以下实例默认运行环境都为Standard mode): 如何让层在falsh上显示? 方法: ``` 设置flash的wmode值为transparent或opaque ``` ...
- Centos的APK解包打包签名
http://www.v5b7.com/other/apk.html vi /etc/profile PATH=/usr/local/mysql/bin:/usr/local/mysql/lib:/u ...
- nginx安全日志分析脚本的编写
https://blog.csdn.net/nextdoor6/article/details/51914966
- HttpRunner 接口自动化测试进阶
前面说到了httprunner的安装与简单使用,参见: https://www.cnblogs.com/chengtch/p/8735160.html 这里我们介绍一下通过调试源码的方式来做接口测试: ...
- Hadoop之Vmware通过仅Use Host-Only networking(使用主机网络)主机链接
Use Host-Only networking(使用主机网络)连接方式 [1]现在宿主机也就是本地电脑上设置IP地址 [2]设置虚拟机 Host-Only 方式 验证 宿 ...
- HDU 3237 Tree(树链剖分)(线段树区间取反,最大值)
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 9123 Accepted: 2411 Description ...
- mysql里的知识
1.mysql基础 (1)mysql存储结构:数据库->表-> 数据 sql语句 (2)管理数据库: 增加: create database 数据库 default character ...
- Xamarin XAML语言教程使用Progress属性数据绑定设置进度条进度
Xamarin XAML语言教程使用Progress属性数据绑定设置进度条进度 开发者除了可以为ProgressBar定义的Progress属性直接赋双精度类型的值外,还可以通过数据绑定的方式为该属性 ...
- TCP/IP,HTTP,SOAP等协议之区别
术语TCP/IP代表传输控制协议/网际协议,指的是一系列协议.“IP”代表网际协议,TCP和UDP使用该协议从一个网络传送数据包到另一个网络.把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到 ...