关于八数码问题中的状态判重的三种解决方法(编码、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容器管 ...
随机推荐
- 行为型设计模式之模板方法(Template Method)
结构 意图 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.Te m p l a t e M e t h o d 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 适用性 一次性 ...
- PHP开发
php.ini error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_CORE_WARNING & ~E_NOT ...
- C#中使用7Z进行压缩解压
SevenZipSharp相关文档下载地址: http://sevenzipsharp.codeplex.com/releases/view/51254 1. 解决方案中添加引用:SevenZipSh ...
- PHP防止表单重复提交的解决方法
PHP+SESSION防止表单重复提交 index.php 当前表单页面is_submit设为0 SESSION_START(); $_SESSION['is_submit'] = 0; <fo ...
- HDU6383 2018 “百度之星”程序设计大赛 - 初赛(B) 1004-p1m2 (二分)
原题地址 p1m2 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total ...
- Controlled Tournament(状态压缩DP)
Controlled Tournament 题意 n 名选手进行淘汰赛,R[i][j] = 1 表示 i 能胜过 j.要求通过安排淘汰赛使得,m 选手获得最终胜利,问使得比赛数最少的方案数. 分析 设 ...
- 洛谷——P1991 无线通讯网
P1991 无线通讯网 题目描述 国防部计划用无线网络连接若干个边防哨所.2 种不同的通讯技术用来搭建无线网络: 每个边防哨所都要配备无线电收发器:有一些哨所还可以增配卫星电话. 任意两个配备了一条卫 ...
- JSK 11: 移除数组中的重复元素
题目描述 给定一个升序排列的数组,去掉重复的数,并输出新的数组的长度. 例如:数组 $A = \{1, 1, 2\}$,你的程序应该输出 $2$ 即新数组的长度,新数组为 $\{1, 2\}$. 要求 ...
- POJ 3977:Subset(折半枚举+二分)
[题目链接] http://poj.org/problem?id=3977 [题目大意] 在n个数(n<36)中选取一些数,使得其和的绝对值最小. [题解] 因为枚举所有数选或者不选,复杂度太高 ...
- 常用SQL收藏
原文:常用SQL收藏 MSSQL Split表字段 --拆分字符串之后匹配结果集合 CREATE FUNCTION [dbo].[fnSplit]( @sInputList VARCHAR(8000) ...