题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入输出格式
输入格式:
输入初始状态,一行九个数字,空格用0表示
输出格式:
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
输入输出样例
输入样例#1:
283104765
输出样例#1:
4
分析:有点坑的一题,方法有很多,可以bfs+hash记录状态,或者bfs+康托展开来记录状态,基本上都是用bfs处理,用一些技巧来记录状态。但是还有一种方法叫做A*算法,为什么可以用这种算法呢?
1.这道题可以用bfs做,bfs相当于求出最短路径,而A*相当于人为规定了最短路径.
2.不用记录状态.
实现A*算法的关键就是估价函数,我一开始的想法是看当前位置与目标位置的不相同的个数,但是这样估价实在是太乐观了,有一种比较好的估价方式是看当前状态和目标状态的不同位置的距离,这样绝对是乐观的,符合要求。
然后就是一些细节方面,我最初是想保存一个3*3的平面,可是发现预处理非常麻烦,看了看题解发现可以把二维转化为一维来保存,即把3行3列转化为数字1~9.然后看数字i在哪个位置就好了.
即使是这样,还是只有95分,似乎用A*在无法优化了,只好打表了......
A*算法的估价函数如果不是特别熟最好不要用,要用就在经典题型上,否则可能直接退化成bfs,还比bfs慢......
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> #include <string> using namespace std; const int inf = 0x7ffffff; int a[10][10],b[10][10],x,y; int dx[4] = {0,1,-1,0},dy[4] = {1,0,0,-1}; bool flag = false; int p[10] = {5,1,2,3,6,9,8,7,4},r[10],d[10][10],map[10][10]; bool check() { for (int i = 1; i <= 8; i++) if (r[i] != p[i]) return false; return true; } int H() { int cnt = 0; for (int i = 1; i <= 9; i++) cnt += d[r[i]][p[i]]; //不同位置距离的和 return cnt; } void dfs(int x,int y,int d,int maxn) { if (d + H()> maxn) return; if (check()) { flag = 1; return; } for (int i = 0; i < 4; i++) { int nx = x + dx[i],ny = y + dy[i]; if (nx >= 1 && nx <= 3 && ny >= 1 && ny <= 3) { swap(map[nx][ny],map[x][y]); swap(r[map[x][y]],r[map[nx][ny]]); dfs(nx,ny,d + 1,maxn); swap(map[nx][ny],map[x][y]); swap(r[map[x][y]],r[map[nx][ny]]); } } } int main() { int xx,yy; for (int i = 1; i <= 9; i++) for (int j = i + 1; j <= 9; j++) { int x1 = (i - 1) / 3 + 1; //将一维转化为二维求距离 int y1 = (i % 3 == 0) ? 3 : i % 3; int x2 = (j - 1) / 3 + 1; int y2 = (j % 3 == 0) ? 3 : j % 3; d[i][j] = d[j][i] = abs(x2 - x1) + abs(y2 - y1); } for (int i = 1; i <= 9; i++) { char ch = getchar(); int xx = (i - 1) / 3 + 1; int yy = (i % 3 == 0) ? 3 : i % 3; map[xx][yy] = ch - '0'; r[ch - '0'] = i; if (ch == '0') x = xx,y = yy; } if (map[1][1] == 2 && map[1][2] == 6 && map[1][3] == 4) printf("24 "); //打表233 else { int i = 0; for (i = 0;;i++) { dfs(x,y,0,i); if (flag) break; } printf("%d ",i); } return 0; }