http://acm.hdu.edu.cn/showproblem.php?pid=1043
转载请注明出处,谢谢http://blog.youkuaiyun.com/ACM_cxlove?viewmode=contents by---cxlove
第一个A*搜索,A*是一种启发式搜索,g为已花代价,h为估计的剩余代价,而A*是根据f=g+h作为估价函数进行排列,也就是优先选择可能最优的节点进行扩展。
对于八数码问题,以下几个问题需要知道
判断有无解问题:根据逆序数直接判断有无解,对于一个八数码,依次排列之后,每次是将空位和相邻位进行调换,研究后会发现,每次调换,逆序数增幅都为偶数,也就是不改变奇偶性,所以只需要根据初始和目标状态的逆序数正负判断即可。
HASH问题:根据的是康托展开,具体证明请找网上资料
以及估价函数H:是根据与目标解的曼哈顿距离,也就是每个数字与目标位置的曼哈顿距离之和。
以了以上的基础,便可以通过A*解决八数码问题。
对于这题,实验了下,优先队列第一关键字为f,第二关键字为h,耗时2s+,第一关键字为f,第二关键字为g,耗时1s+,第一关键字为h,第二关键字为g,耗时450ms左右。在搜索过程中,加上判断是否有解,时间变化不大。POJ上0ms
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<iostream>
#include<map>
#include<vector>
#include<string>
using namespace std;
struct Node
{
int maze[3][3];
int h, g;//两个估价函数
int x, y; //空格所在的位置
int Hash; //HASH值
bool operator<(const Node n1)const
{
return h != n1.h ? h > n1.h : g > n1.g;
}
bool check()
{
if (x >= 0 && x < 3 && y >= 0 && y < 3)
return true;
return false;
}
}s,u,v,tt;
int HASH[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int destination = 322560;//目标情况的HASH值
int vis[400000];//判断状态已遍历,初始为-1,否则为到达这步的转向
int pre[400000];//保存路径
int way[4][2] = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
int get_hash(Node tmp)
{
int a[9], k = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
a[k++] = tmp.maze[i][j];
}
int res = 0;
for (int i = 0; i < 9; i++)
{
k = 0;
for(int j = 0; j < i; j++)
{
if (a[j]>a[i])
k++;
}
res += HASH[i] * k;
}
return res;
}
bool isok(Node tmp) //求逆序对,判断是否有解
{
int a[9], k = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
a[k++] = tmp.maze[i][j];
}
int sum = 0;
for (int i = 0; i < 9; i++)
{
for (int j = i + 1; j < 9; j++)
if (a[j] && a[i] && a[i] > a[j])
sum++;
}
return !(sum&1); //由于目标解为偶数,所以状态的逆序数为偶数才行
}
int get_h(Node tmp) //曼哈顿距离
{
int ans = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (tmp.maze[i][j])
ans += abs(i - (tmp.maze[i][j] - 1) / 3) + abs(j - (tmp.maze[i][j] - 1) % 3);
return ans;
}
void astar()
{
priority_queue<Node>que;
que.push(s);
while (!que.empty())
{
u = que.top();
que.pop();
for (int i = 0; i < 4; i++)
{
v = u;
v.x += way[i][0];
v.y += way[i][1];
if (v.check())
{
swap(v.maze[v.x][v.y],v.maze[u.x][u.y]);
v.Hash = get_hash(v);
if (vis[v.Hash] == -1 && isok(v))
{
vis[v.Hash] = i; //保存方向
v.g++;
pre[v.Hash] = u.Hash; //保存路径
v.h = get_h(v);
que.push(v);
}
if (v.Hash == destination)
return ;
}
}
}
}
void print()
{
string ans;
ans.clear();
int nxt = destination;
while (pre[nxt] != -1)
{
switch (vis[nxt])
{
case 0:ans += 'r'; break;
case 1:ans += 'l'; break;
case 2:ans += 'd'; break;
case 3:ans += 'u'; break;
}
nxt = pre[nxt];
}
for (int i = ans.size() - 1; i >= 0; i--)
putchar(ans[i]);
puts("");
}
int main()
{
char str[100];
while (gets(str) != NULL)
{
int k = 0;
memset(vis,-1,sizeof(vis));
memset(pre,-1,sizeof(pre));
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if ((str[k] <= '9'&& str[k] >= '0') || str[k] == 'x')
{
if (str[k] == 'x')
{
s.maze[i][j] = 0;
s.x = i;
s.y = j;
}
else
s.maze[i][j] = str[k] - '0';
}
else
j--;
k++;
}
}
if (!isok(s))
{
printf("unsolvable\n");
continue;
}
s.Hash = get_hash(s);
if (s.Hash == destination)
{
puts("");
continue;
}
vis[s.Hash] = -2;
s.g = 0;
s.h = get_h(s);//puts("hello");
astar();
print();
}
return 0;
}