八数码问题搜索有非常多高效方法:如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、&lt;set&gt;)的更多相关文章

  1. fluent中UDF环境变量问题的三种解决方法

    方法一: 这种方式最简便,首选这种,但是有时会因为不明原因而不好使,我自己电脑刚开始用这种方式是行得通的,但是后来中途装过很多乱七八糟的软件,估计环境变量改乱了,这时候只能用第二种或者第三种方法.先说 ...

  2. [转]PHP开发中涉及到emoji表情的三种处理方法

    最近几个月做微信开发比较多,存储微信昵称必不可少,可这万恶的微信支持emoji表情做昵称,这就有点蛋疼了 一般Mysql表设计时,都是用UTF8字符集的.把带有emoji的昵称字段往里面insert一 ...

  3. PHP中出现Notice: Undefined index的三种解决办法

    前一段做的一个PHP程序在服务器运行正常,被别人拿到本机测试的时候总是出现“Notice: Undefined index:”这样的警告,这只是一个因为PHP版本不同而产生的警告(NOTICE或者WA ...

  4. Springboot中关于跨域问题的一种解决方法

    前后端分离开发中,跨域问题是很常见的一种问题.本文主要是解决 springboot 项目跨域访问的一种方法,其他 javaweb 项目也可参考. 1.首先要了解什么是跨域 由于前后端分离开发中前端页面 ...

  5. mysqli:查询数据库中,是否存在数据的三种校验方法

    在我们编辑用户登录功能的时候,常常需要对用户输入的信息进行校验,校验的方法就是通过SQL语句进行一个比对,那么我们就需要用到以下三种中的一种进行校验啦 1.使用mysqli_num_rows()校验 ...

  6. .NET中TextBox控件设置ReadOnly=true后台取不到值三种解决方法

    当TextBox设置了ReadOnly=true后要是在前台为控件添加了值,后台是取不到的,值为空,多么郁闷的一个问题经过尝试,发现可以通过如下的方式解决这个问题.感兴趣的朋友可以了解下 当TextB ...

  7. .NET中TextBox控件设置ReadOnly=true后台取不到值 三种解决方法

    方法一:不设置ReadOnly属性,通过onfocus=this.blur()来模拟,如下: <asp:TextBox ID="TextBox1" runat="s ...

  8. Electron与jQuery中$符号冲突的三种解决方法

    在Electron工程中引用jQuery时,经常会出现以下错误: Uncaught ReferenceError: $ is not defined 解决的具体方法如下: ①.在测试的过程中(测试过1 ...

  9. listener中@Autowired无法注入bean的一种解决方法

    背景:使用监听器处理业务,需要使用自己的service方法: 错误:使用@Autowired注入service对象,最终得到的为null: 原因:listener.fitter都不是Spring容器管 ...

随机推荐

  1. 倒置函数reverse的用法

    倒置字符串函数reverse:用于倒置字符串s中的各个字符的位置,如原来字符串中如果初始值为123456,则通过reverse函数可将其倒置为654321,程序如下:#include<stdio ...

  2. 创建型设计模式之建造者模式(Builder)

    结构 意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 适用性 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时. 当构造过程必须允许被构造的对象有不 ...

  3. [ CodeVS冲杯之路 ] P1091

    不充钱,你怎么AC? 题目:http://codevs.cn/problem/1091/ 大家都写的 DFS,然而我想到了一种贪心的做法,重点是可以A 普遍的贪心是每次删掉该深度子树最大的点,但是如果 ...

  4. 简单粗暴!解决锐捷强制关闭VMware NAT Service的问题(图文教程)

    众所周知毒瘤的锐捷会定时强制关闭NAT服务,导致虚拟机连不上网,进而你的虚拟机就成了孤儿,只能玩单机. 在一番百度后,得到了一种神仙破解办法.原理是把锐捷关闭服务时所用的搜索关键字改掉,这样锐捷就搜不 ...

  5. wav格式

    转自: http://www.cnblogs.com/tiandsp/archive/2012/10/17/2728585.html 起始地址 占用空间 本地址数字的含义 00H 4byte RIFF ...

  6. qfish/Bee-Xcode-Template

    https://github.com/qfish/Bee-Xcode-Template Bee-Xcode-Template Xcode Template for BeeFramework. You ...

  7. dpkg: error processing package bluez (--configure) 解决方法【转】

    转自:http://blog.csdn.net/heray1990/article/details/47803541 在 Ubuntu 执行 sudo apt-get upgrade 时,出现了如下的 ...

  8. android hook 框架 libinject2 简介、编译、运行

    Android so注入-libinject2 简介.编译.运行 Android so注入-libinject2  如何实现so注入 Android so注入-Libinject 如何实现so注入 A ...

  9. Linux内核情景分析之异常访问,用户堆栈的扩展

    情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...

  10. Apache开启PHP的伪静态模式

    首先,什么是伪静态: 伪静态又名URL重写,是动态的网址看起来像静态的网址.换句话说就是,动态网页通过重写 URL 方法实现去掉动态网页的参数,但在实际的网页目录中并没有必要实现存在重写的页面. 1. ...