八数码的A*与IDA*算法-搜索进阶练习1
Posted ling_xiao007
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八数码的A*与IDA*算法-搜索进阶练习1相关的知识,希望对你有一定的参考价值。
八数码的A*与IDA*算法-搜索进阶练习1
hdu1043:http://acm.hdu.edu.cn/showproblem.php?pid=1043
poj1077:http://poj.org/problem?id=1077
题意:众所周知的八数码问题,就不再描述了。不得不说,为了练习A*以及IDA*就直接看题解了。 了解之后分析如下。
解题报告:
先谈A*。A*是一种 启发式搜索,也就是说在每次搜索前,先进行在状态空间中对每一个搜索的位置通过f(n) = g(n) + h(n)进行评估,得到最好的位置,再从这个位置,
然后是IDA*。A*算法和迭代加深算法的结合。
1.搜索框架? A*(IDA*)算法。搜索直到终态结束,或该状态非法进行下一状态。 2.状态? 直接记录每个状态的maze、x位置信息以及A*所需f、g、h。同时使用hash值指向所有状态。 3.状态转移? x与上下左右互换,更新状态信息。 4.剪枝? 一个简单有效的剪枝逆序数为奇则必定无解。 5.hash以及h? hash可通过各位置逆序数乘位置散列值之和散列。h为所有数字块的曼哈顿距离和。
具体而言。A*通过构造优先队列实现搜索。构造优先队列时当f相同时按照g值从大到小排序,这样又是一个很给力的减枝。
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
//A*: 因为每次移动都会影响一个点的曼哈顿距离(不算x)
//构造h()为所有数字块的曼哈顿距离和,用逆序数hash(算x)
//根据逆序数奇偶性(不算x)减掉无法到达的情况,
struct Node
char maze[3][3];
int r, c, f, g, h; //(x, y); f = g + h
/*f(n) 是从初始点经由节点n到目标点的估价函数,
g(n) 是在状态空间中从初始节点到n节点的实际代价,
h(n) 是从n到目标节点最佳路径的估计代价。*/
Node()
Node(const Node & rhs)
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
maze[i][j] = rhs.maze[i][j];
r = rhs.r, c = rhs.c, g = rhs.g, f = rhs.f, h = rhs.h;
//估价函数小的先出队,相同则实际代价大的先出队
friend bool operator < (const Node & a, const Node & b)
if (a.f == b.f) return a.g < b.g;
return a.f > b.f;
st;
const int dir[4][2] = 1,0 , 0,1 , -1,0 , 0,-1 ;//方向
char dr[5] = "drul"; //方向查找表
int pos[9][2] = 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2; //位置信息
int fac[9] = 1, 1, 2, 6, 24, 120, 720, 5040, 40320 ; //hash散列因子
char path[370000];
bool vis[370000];
int pre[370000];
int shash;
int h(const Node s) //估价函数,偏移步数
int ans = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (s.maze[i][j] == 'x') continue;
int c = s.maze[i][j] - '1';
int x = pos[c][0], y = pos[c][1];
ans += (abs(x - i) + abs(y - j));
return ans;
int hash_node(const Node s) //hash散列,储存状态
int ans = 0;
char str[10];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
int cnt = 0;
str[i * 3 + j] = s.maze[i][j];
for (int k = 0; k < i * 3 + j; k++)
if (str[k] > str[i * 3 + j]) cnt++;
ans += fac[i * 3 + j] * cnt;
return ans;
bool check_h() //逆序为奇返回true
int cnt = 0;
char str[10];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
str[i * 3 + j] = st.maze[i][j];
if (st.maze[i][j] == 'x') continue;
for (int k = 0; k < i * 3 + j; k++)
if (str[k] == 'x') continue;
if (str[k] > str[i * 3 + j]) cnt++;
return cnt & 1;
bool bfs()
priority_queue<Node> q;
q.push(st);
memset(vis, false, sizeof(vis));
vis[shash] = true;
while (!q.empty())
Node u = q.top(); q.pop();
int ihu = hash_node(u);
for (int d = 0; d < 4; d++)
Node v(u);
v.r = u.r + dir[d][0], v.c = u.c + dir[d][1];
if (v.r < 0 || v.r >= 3 || v.c < 0 || v.c >= 3) continue;
//更新地图
v.maze[u.r][u.c] = v.maze[v.r][v.c];
v.maze[v.r][v.c] = 'x';
v.g++, v.h = h(v); v.f = v.g + v.h; //更新f = g + h
int ihv = hash_node(v);
if (vis[ihv]) continue;
vis[ihv] = true; pre[ihv] = ihu;
path[ihv] = dr[d];
if (ihv == 0) return true; //搜索结束
q.push(v);
return false;
void print(int h)
if (h != shash)
print(pre[h]);
putchar(path[h]);
char in[100];
int main()
while (gets_s(in))
for (int i = 0, x = 0, y = 0; in[i]; i++)
if ((in[i] <= '8' && in[i] >= '1') || in[i] == 'x')
st.maze[x][y] = in[i];
if (in[i] == 'x') st.r = x, st.c = y;
y++;
if (y == 3) x++, y = 0;
if (check_h()) puts("unsolvable"); continue;
st.g = 0; st.h = h(st); st.f = st.h;
if ((shash = hash_node(st)) == 0) puts(""); continue;
int thash = bfs();
print(0); //递归输出答案
puts("");
return 0;
而IDA*的话,和A*相同的h()函数。当前h深度下一层一层地进行搜索。
#include <cstdio>
#include <algorithm>
using namespace std;
struct Node
int r, c, f, g, h;
char maze[3][3];
Node()
Node(const Node & rhs)
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
maze[i][j] = rhs.maze[i][j];
r = rhs.r, c = rhs.c;
ts, s;
const int maxn = 370000;
const int dir[4][2] = 1,0 , 0,1 , -1,0 , 0,-1 ;
const char dr[5] = "drul";
const int fac[9] = 1,1,2,6,24,120,720,5040,40320 ;
const int pos[][2] = 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2 ;
char path[maxn];
bool vis[maxn];
int deep;
int h(const Node st)
int ans = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (st.maze[i][j] == 'x') continue;
int c = st.maze[i][j] - '1';
ans += abs(pos[c][0] - i) + abs(pos[c][1] - j);
return ans;
int mhash(const Node st)
char str[10];
int ans = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
str[i * 3 + j] = st.maze[i][j];
int cnt = 0;
for (int k = 0; k < 3 * i + j; k++)
if (str[k] > str[i * 3 + j]) cnt++;
ans += cnt * fac[i * 3 + j];
return ans;
bool check() //剪枝, 逆序数为奇返回1
char str[10];
int cnt = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
str[i * 3 + j] = s.maze[i][j];
if (s.maze[i][j] == 'x') continue;
for (int k = 0; k < 3 * i + j; k++)
if (str[k] == 'x') continue;
if (str[k] > str[i * 3 + j]) cnt++;
return cnt & 1;
int dfs(int d)
//搜索完成
if (h(ts) == 0) return true;
//超过当前的搜索深度
if (h(ts) + d > deep) return false;
//当前r、c
int r = ts.r, c = ts.c;
for (int dd = 0; dd < 4; dd++)
int nr = ts.r + dir[dd][0], nc = ts.c + dir[dd][1];
if (nr < 0 || nc < 0 || nr > 2 || nc > 2) continue;
ts.maze[r][c] = ts.maze[nr][nc];
ts.maze[nr][nc] = 'x';
int ihv = mhash(ts);
if (vis[ihv])
ts.maze[nr][nc] = ts.maze[r][c];
ts.maze[r][c] = 'x';
continue;
vis[ihv] = true;
ts.r = nr, ts.c = nc;
path[d] = dr[dd];
if (dfs(d + 1)) return true;
vis[ihv] = false;
ts.r = r, ts.c = c;
ts.maze[nr][nc] = ts.maze[r][c];
ts.maze[r][c] = 'x';
return false;
int main()
char in[100];
while (gets_s(in))
for (int i = 0, x = 0, y = 0; in[i]; i++)
if (in[i] == 'x' || (in[i] > '0' && in[i] < '9'))
if (in[i] == 'x') s.c = y, s.r = x;
s.maze[x][y++] = in[i];
if (y == 3) y = 0, x++;
if (check()) puts("unsolvable"); continue;
memset(vis, false, sizeof(vis));
vis[mhash(s)] = true;
ts = s; deep = 0;
while (true)
if (dfs(0)) break;
deep++;
for (int i = 0; i < deep; i++)
putchar(path[i]);
puts("");
return 0;
最后打卡一下。之前Ubuntu的ibus默认的中文输入法双拼的,当时就感觉是一堆乱码,玩毛。不几天前,尝试用双拼了。
以上是关于八数码的A*与IDA*算法-搜索进阶练习1的主要内容,如果未能解决你的问题,请参考以下文章