【问题描述】
任意选择一张图片,将其切分成MxN个小块,去掉其中任意一块,然后打乱顺序。游戏规则是:经过若干步移动从而复原原始图片中小块的顺序,每次移动可以将与空格相邻的小块移动到空格处。
例如,选择图1所示的图片;将其切分为3x3个小块,去掉最后一块,形成图2所示的样子;随机打乱顺序后,形成图3的样子;将图1中9个小块按先行后列的顺序编号为0~8,8号小块被去掉,用-1表示空格,则图3就可对应为图4的矩阵。
图3的状态有两种合法的移动方式,分别如图5和图6所示。其中,将图4中空格上方的小块下移形成了图5,将图4中空格左侧的小块右移形成了图6。游戏时,需要通过若干步合法的移动,使得状态变为图7所示。
请给出使用优先队列搜索算法求解拼图游戏的【问题分析】和【算法伪代码】。
【问题分析】
这是一个八数码问题,在一个3*3的棋盘上,随机放置0-8的数字,去掉一个数字,出现空位,用-1来填补,数字可以移动到空位,经过若干次移动后,可以实现指定目标。
可以采用广度优先搜索算法遍历实现功能。
先构造一个优先队列,然后从空白格周围开始搜索,从上下左右四个方向找到可交换的位置,进行交换,然后把交换之后的状态放入优先队列中,从队列中取出队头元素,得到新的状态,将该状态与目标状态进行比较,若一样,则结束并返回此时交换次数,若不同,重复以上步骤,继续进行搜索。将交换后满足要求的状态入队,并标记访问,需要展开判重。
【算法伪代码】
//展开判重
Cantor(int str[], int n)
sum = 0;
for (int i = 0; i < n; i++)
{
int cnt = 0;
for (int j = i + 1; j < n; j++)
if (str[i] > str[j])
cnt++;
sum += cnt * factory[n - i - 1];
}
return sum;
//广度优先搜索算法
int bfs()
Node head;
memcpy(head.state, startstate, sizeof(startstate));
head.step = 0;
q.push(head);
temp = 0;
while (!q.empty())
{
temp++;
head = q.top();
q.pop();
if (memcmp(head.state, goalstate, sizeof(goalstate)) == 0)
return head.step;
if (temp >= 1000)
return -1;
int k;
for (k = 0; k < n * m; k++)
if (head.state[k] == -1)
break;
int x = k % m;
int y = k / m;
for (int i = 0; i < 4; i++)
{
int newx = x + xx[i];
int newy = y + yy[i];
int newpos = newx + m * newy;
if (newx >= 0 && newx < n && newy >= 0 && newy < m)
Node newnode;
memcpy(&newnode, &head, sizeof(Node));
Swap(newnode.state[k], newnode.state[newpos]);
newnode.step++;
newcan = Cantor(newnode.state, n * m);
if (!vis[newcan]) q.push(newnode);vis[newcan] = true;
}
}
return -1;
【代码实现】
#include <bits/stdc++.h>
using namespace std;
const int n = 3;
const int m = 3;
const int len = 1000000;
int xx[4] = { -1,0,1,0 };
int yy[4] = { 0,-1,0,1 };
bool vis[len];
int startstate[n * m];
int goalstate[n * m];
long factory[n * m+1];
struct Node
{
int state[9];
int step;
};
int f(int k)
{
int sum = 1;
for (int i = 1; i <= k; i++)
sum = sum * i;
return sum;
}
int difnum(Node a)
{
int num = 0;
for (int i = 0; i < n * m; i++)
if (a.state[i] != goalstate[i])
num++;
return num;
}
struct cmp
{
bool operator()(Node& a, Node& b)
{
return a.step + difnum(a) > b.step + difnum(b);
}
};
//优先队列
priority_queue<Node, vector<Node>, cmp> q;
//展开判重
int Cantor(int str[], int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
int cnt = 0;
for (int j = i + 1; j < n; j++)
if (str[i] > str[j])
cnt++;
sum += cnt * factory[n - i - 1];
}
return sum;
}
//数据初始化,输入数据
void Init()
{
int posn, posm;
cin >> posn >> posm;
for (int i = 0; i < n * m; i++)
cin >> startstate[i];
for (int i = 0; i < n * m; i++)
goalstate[i] = i;
goalstate[posn * m + posm] = -1;
factory[1] = 1;
for (int i = 1; i <= 9; i++)
{
factory[i] = f(i);
}
}
void Swap(int &a, int &b)
{
int c;
for (int i = 0; i < n*m ; i++)
{
c = a;
a = b;
b = c;
}
}
//广度遍历
int bfs()
{
Node head;
memcpy(head.state, startstate, sizeof(startstate));
head.step = 0;
q.push(head);
int temp = 0;
while (!q.empty())
{
temp++;
head = q.top();
q.pop();
if (memcmp(head.state, goalstate, sizeof(goalstate)) == 0)
return head.step;
if (temp >= 1000)
return -1;
int k;
for (k = 0; k < n * m; k++)
if (head.state[k] == -1)
break;
int x = k % m;
int y = k / m;
for (int i = 0; i < 4; i++)
{
int newx = x + xx[i];
int newy = y + yy[i];
int newpos = newx + m * newy;
if (newx >= 0 && newx < n && newy >= 0 && newy < m)
{
Node newnode;
memcpy(&newnode, &head, sizeof(Node));
Swap(newnode.state[k], newnode.state[newpos]);
newnode.step++;
int newcan = Cantor(newnode.state, n * m);
if (!vis[newcan])
{
q.push(newnode);
vis[newcan] = true;
}
}
}
}
return -1;
}
int main()
{
Init();
cout << bfs() << endl;
return 0;
}