算法笔记BFS篇:【宽搜入门】8数码难题

算法笔记BFS篇:【宽搜入门】8数码难题_第1张图片
算法笔记BFS篇:【宽搜入门】8数码难题_第2张图片
说真的,这道题我不会写。。。
题解:

看了网上挺多文章,自己也琢磨挺久的,终于在深夜搞定了。

关键思想:用map实现映射

参考别人的博客:

一、状态如何表示?
1.每个状态都用3*3的数组表示,但是BFS中需要入队出队,比较麻烦而且空间占用较大

2.状态压缩,采用一个整数保存状态的数字序列,例如状态1表示为283104765,状态2表示为203184765

二、如何判重?
1.如果空间允许,开一个876543210大小的bool数组,某个序列出现就将数组值置为1;但是竞赛中一般都是限制128M(大约10000000),这个数组开不下来。

2.虽然状态范围是012345678–876543210,但是中间真正有效的只有9!=362800,因为数字不可能出现重复;因此可以考虑开一个数组大小为9!整型数组A和bool数组B,然后生成0-8这9个数码的全排列并按照升序或者降序存入数组中,要判断某个状态(一种排列方式)是否出现过,直接通过二分查找的方式找到该排列在A中的下标i,然后查看数组B[i]为true还是false;如果为true则出现过,如果为false则将状态入队,并设置B[i]=true;

3.其实从方案2中我们已经看到,判重的实质就是建立状态数字串(一个int数据)和是否出现(一个bool数据)之间的联系,而STL中刚好提供了map这样一种容器,我们可以将状态数字串作为key,是否出现作为value直接建立起状态–是否出现的联系。

4.使用hash判重,将状态数字串通过某种映射f(x)从012345678–876543210这样一个大集合,映射到128M范围之内;这里采用简单的hash,取模一个大质数,只要这个质数大于9!即可;当然这里可能出现冲突,也就是key1!=key2但是f(key1)==f(key2),hash算法只能减少冲突不能避免冲突。这里如何减少冲突呢?挂链表,当key1!=key2但是f(key1)==f(key2),则将key2挂到key1后面;当然这里如果使用康托展开可以完美一一映射而不冲突,但是我不会(^)。

我选择的方案三

AC代码:

#include
#include
#include
#include
using namespace std;
struct node
{
     
	int a[3][3];
	int x0, y0;  //记录0的位置
	int step;	//记录步数
};
map <int, bool> M;  //用map实现映射
int my_hash(int t[3][3])   //映射/标志
{
     
	int p = 0;
	for (int i = 0; i < 3; i++)
	{
     
		for (int j = 0; j < 3; j++)
		{
     
			p = p * 10 + t[i][j];
		}
	}
	return p;
}
bool next_sta(node &t, int n)  //交换
{
     
	int x = t.x0, y = t.y0;
	if (n == 0 && x > 0)  //上
	{
     
		swap(t.a[x][y], t.a[x - 1][y]);
		t.x0 = t.x0 - 1;
		return true;
	}
	if (n == 1 && x <2)  //下
	{
     
		swap(t.a[x][y], t.a[x + 1][y]);
		t.x0 = t.x0 + 1;
		return true;
	}
	if (n == 2 && y>0)  //左
	{
     
		swap(t.a[x][y], t.a[x][y-1]);
		t.y0 = t.y0 - 1;
		return true;
	}
	if (n == 3 && y<2)  //右
	{
     
		swap(t.a[x][y], t.a[x][y + 1]);
		t.y0 = t.y0 + 1;
		return true;
	}
	return false;
 
}
int main()
{
     
	node start, end;
	
	for (int i = 0; i < 3; i++) //输出初始状态
	{
     
		for (int j = 0; j < 3; j++)
		{
     
			cin >> start.a[i][j];
			if (start.a[i][j] == 0)
			{
     
				start.x0 = i;
				start.y0 = j;
			}
		}
	}
	for (int i = 0; i < 3; i++)  //目标状态
	{
     
		for (int j = 0; j < 3; j++)
			cin >> end.a[i][j];
	}
 
	queue <node>Q;
	start.step =1, end.step = 0;
	Q.push(start);
	M[my_hash(start.a)] = 1;
	int flag = 0;
	while (!Q.empty())
	{
     
		node vn = Q.front();
		Q.pop();
		for (int i = 0; i < 4; i++)  //0,1,2,3分别代表上下左右
		{
     
			node temp = vn;
			if (next_sta(temp, i))   //判断该交换是否可行
			{
     
				if (M[my_hash(temp.a)] == 0)  //该状态未出现过
				{
     
					M[my_hash(temp.a)] = 1;
					temp.step++;
					if (my_hash(temp.a) == my_hash(end.a))  //与最终结点的值相同
					{
     
						end.step = temp.step;
						flag = 1;
						break;
					}
					Q.push(temp);
				}
			}
		}
		if (flag)break;
		
 
	}
	cout << end.step << endl;
	return 0;
}

你可能感兴趣的:(算法笔记)