(隐式图)八数码问题(三种判重方法:排列计数、哈希技术、STL_set判重)

#include
#include
#include
#include
using namespace std;

typedef int State[9];
const int maxstate = 1000000;
State st[maxstate], goal;  //这个st数组就像一个队列  有front队首和rear队尾 
int dist[maxstate];

const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
//=========================================================== 
//!!!!!使用的三种判重的方法!!!! 
//=========================================================== 
//1、查找表法:(将0~8的全排列和0~362879一一对应起来) 
//优点:采用排列计数方法,好写
//缺点:如果总结点数非常大,编码就会很大,数组还是开不下 
/*
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; //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++;
		//在i后面比i小的元素的个数*(8-i)的阶乘就是确定前面的数在确定
		//了的情况下已经产生的可能的排法
		code += fact[8-i] * cnt;
		//把每个位上的数字都按上面的操作来一遍就能计算出整个排列所对应的位置 
	}
	if(vis[code]) return 0;
	return vis[code] = 1;
}
*/
//=========================================================== 

//===========================================================
//2、hash计数:
//优点:执行效率高,使用范围广,关键是哈希函数的设计(这个提升效率的关键) 
//缺点:哈希函数如果设计的不好,整个哈希表会退化成少数几条长长的链表(下面我画图举例进行了说明),查找速度会非常缓慢。 

const int hashsize = 1000003;
int head[hashsize], next[maxstate];
void init_lookup_table() {
	memset(head, 0, sizeof(head)); //清空head数组 
} 
int hash(State& s) {  //这个s表示一个状态 
	int v = 0;
	for(int i = 0; i < 9; i++) v = v*10+s[i]; //把九个数字组合成九位数 
	return v % hashsize; //确保hash函数值是不超过hash表的大小的非负整数 
}
int try_to_insert(int s) { //这个s表示一个编号 
	int h = hash(st[s]);  //h表示这个编号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;
} 

//=========================================================== 
//3、set去重法:优点:代码简单,时间紧迫和对效率要求不高时可以使用;缺点:时间效率低;
//使用STL的集合容器set,把状态转化成9位十进制数,就可以用set判重了
/* 
set 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;  //front表示步数  rear表示最后一个添加进去的状态所在的编号 
	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;//找到0的位置
		//z相对于下面是之前的空白块的位置 
		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)); //扩展新结点   //memcpy的速度比for要快! 
				t[newz] = s[z];//传递空白块的位置 
				t[z] = s[newz]; //传递移动了的那个方块 
				dist[rear] = dist[front] + 1;  //移动步数+1 
				if(try_to_insert(rear)) rear++; 
			}
		}
		front++;  //每一层结束都还没有找到 就使步数都加一 
	}
	return 0; 
}

int main() {
	for(int i = 0; i < 9; i++) scanf("%d", &st[1][i]);
	for(int i = 0; i < 9; i++) scanf("%d", &goal[i]);
	int ans = bfs();
	if(ans > 0) printf("%d\n", dist[ans]);
	else printf("-1\n");
	return 0;
}


(隐式图)八数码问题(三种判重方法:排列计数、哈希技术、STL_set判重)_第1张图片

你可能感兴趣的:((隐式图)八数码问题(三种判重方法:排列计数、哈希技术、STL_set判重))