八数码问题

八数码问题

(3*3自动拼图)

不知道是哪个时候,我看到一个自动拼图的特效动画,当时觉得帅炸了,竟然还有这么好玩的东西,心里就开始鼓弄着这到底是怎么做出来的。苦于没时间,我懒,今天认真的用穷举码了下,发现还是很简单的。为什么不用A*算法,3*3好像不需要A*吧。其实是不会~
进入正题:八数码问题怎么做。先图像分离,将图转化成数字。怎么转?很简单。假设一张图(没有打乱)平均分成3*3份,然后依次标记1-9,挖去右下那块(9),并把挖去的那块位置记为0,然后随机打乱。我们的目的就是将他还原。
做法:将0-8看做一个状态,然后穷举。因为0是挖去的位置,所以0这个位置四处移动,得到新的状态,在进行依次迭代,因为要获得最少的步数,所以用宽度优先搜索比较好

//八数码问题并打印路径
#include<bits/stdc++.h>
using namespace std;
const int MAXV = 1000001;
struct Edge
{
	int nums[3][3];
	int now;
	int farther;
	Edge(int mynums[3][3])
	{
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				nums[i][j] = mynums[i][j];
	}
	Edge()
	{

	}
	Edge &operator=(const int mynums[3][3])
	{
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				nums[i][j] = mynums[i][j];
		return *this;
	}
	bool operator<(const Edge &Last)const
	{
		int a, b;
		a = b = 0;
		for(int i=0;i<3;i++)
			for (int j = 0; j < 3; j++)
			{
				a = a * 10 + nums[i][j];
				b = b * 10 + Last.nums[i][j];
			}
		if (a < b)
			return true;
		return false;
	}
	bool operator==(const Edge &Last) const
	{
		int a, b;
		a = b = 0;
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
			{
				a = a * 10 + nums[i][j];
				b = b * 10 + Last.nums[i][j];
			}
		if(a==b)
		return true;
		return false;
	}
};
Edge xx[MAXV];
set<Edge>chongfu;
Edge move_(Edge edge, int direction)
{
	int a=-1, b=-1;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
			if (edge.nums[i][j] == 0)
			{
				a = i;
				b = j;
			}
	if (direction == 0 && a != 0)//上移
		swap(edge.nums[a][b], edge.nums[a - 1][b]);
	else if (direction == 1 && b != 2)
		swap(edge.nums[a][b], edge.nums[a][b + 1]);
	else if (direction == 2 && a != 2)
		swap(edge.nums[a][b], edge.nums[a + 1][b]);
	else if (direction == 3 && b != 0)
		swap(edge.nums[a][b], edge.nums[a][b - 1]);
	Edge temp = edge;
	return temp;
}
bool is_OK(Edge &u)
{
	int k = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			if (u.nums[i][j] != k++)
				return false;
	return true;

}

void display(Edge &u);
int bfs(Edge &first)
{
	int head = 1, tail = 2;
	xx[head] = first;
	xx[head].now = 1;
	xx[head].farther = -1;
	chongfu.insert(first);
	while (head < tail)
	{
		Edge u = xx[head];
		if (is_OK(u) == true)
		{
			break;
		}
		for(int i=0;i<4;i++)
		{
			Edge temp = move_(u, i);
			temp.farther = u.now;
			temp.now = tail;
			if (chongfu.count(temp) == 0)
			{
				
				chongfu.insert(temp);
				xx[tail] = temp;
				tail++;
			}
		}
		head++;
	}
	return head;
}

void display( Edge &u)
{
	cout << "----->" << endl;
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
			cout << u.nums[i][j] << " ";
		cout << endl;
	}

}
int main()
{
	int yy[3][3];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			cin >> yy[i][j];
	Edge u(yy);
	int x=bfs(u);
	while (x != -1)
	{
		display(xx[x]);
		x = xx[x].farther;
	}
}

这里我用set做判重操作,好像hashset就可以,还快,但是我不会用,注意下set存放自定义对象是要重载< ,==,这两个符号,然后如果要打印路径就不能用STL中的queue了,要用原生的数组存放惹。
还有因为是打印路径,所以要将父亲节点记录下来,然后进行回溯。因为是回溯,所以是反的,正过来很简单,不写了。

posted @ 2019-01-31 20:50  ~abc~  阅读(173)  评论(0)    收藏  举报