八数码问题
八数码问题
(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了,要用原生的数组存放惹。
还有因为是打印路径,所以要将父亲节点记录下来,然后进行回溯。因为是回溯,所以是反的,正过来很简单,不写了。
现在很菜,以后更菜

浙公网安备 33010602011771号