八数码问题搜索有非常多高效方法:如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. 学习ExtJS4 常用控件

    ExtJS组件配置方式介绍 1.使用逗号分隔参数列表配置组件    首先来看一个简单的逗号分隔参数列表的例子.这个例子非常简单,它用来显示信息提示框. 2.使用Json对象配置组件  接下来看一个使用 ...

  2. SQL的四种连接查询(转)

    原文转自 https://blog.csdn.net/wangjingna/article/details/48765931

  3. 《Linux命令、编辑器与shell编程》第三版 学习笔记---002

    <Linux命令.编辑器与shell编程>第三版 学习笔记---001 Linux命令.编辑器与shell编程 Shell准备 1.识别Shell类型 echo  $0 echo $BAS ...

  4. Cookies/Session机制详解

    # 转载自:https://blog.csdn.net/fangaoxin/article/details/6952954/ 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话 ...

  5. OutputDebugString方便格式化WIN32封装

    void TRACE(LPCTSTR lpszFmt, ...) { va_list args; va_start(args, lpszFmt); ; TCHAR *lpszBuf = (TCHAR* ...

  6. 中控考勤机WEB主动上报接收SERVER程序

    using System; using System.IO; using System.Net; using System.Text.RegularExpressions; namespace Con ...

  7. C#中使用7Z进行压缩解压

    SevenZipSharp相关文档下载地址: http://sevenzipsharp.codeplex.com/releases/view/51254 1. 解决方案中添加引用:SevenZipSh ...

  8. linq中转换类型报错

    错误:LINQ to Entities 不识别方法“Int32 ToInt32(System.String)”,因此该方法无法转 上面报错是因为在Linq表达式中无法识别Convert和Parse方法 ...

  9. HDU 2795.Billboard-完全版线段树(区间求最值的位置、区间染色、贴海报)

    HDU2795.Billboard 这个题的意思就是在一块h*w的板子上贴公告,公告的规格为1*wi ,张贴的时候尽量往上,同一高度尽量靠左,求第n个公告贴的位置所在的行数,如果没有合适的位置贴则输出 ...

  10. TCP/IP,HTTP,SOAP等协议之区别

    术语TCP/IP代表传输控制协议/网际协议,指的是一系列协议.“IP”代表网际协议,TCP和UDP使用该协议从一个网络传送数据包到另一个网络.把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到 ...