博客
关于我
P1379 八数码难题 ( A* 算法 与 IDA_star 算法)
阅读量:398 次
发布时间:2019-03-06

本文共 3223 字,大约阅读时间需要 10 分钟。

P1379 八数码难题

题目描述

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入格式

输入初始状态,一行九个数字,空格用0表示

输出格式

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

输入输出样例

输入 #1

283104765

输出 #1

4

分析:

利用A-star(A*算法),设目标状态为

123804765

\(h\) 函数可以定义为,不在应该在的位置的数字个数。

容易发现 \(h\) 满足以上两个性质,此题可以使用 A*算法求解。

代码实现:

#include 
#include
#include
#include
#include
using namespace std;const int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};int fx, fy;char ch;struct matrix { int a[5][5]; bool operator<(matrix x) const { for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) if (a[i][j] != x.a[i][j]) return a[i][j] < x.a[i][j]; return false; }} f, st;int h(matrix a) { int ret = 0; for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) if (a.a[i][j] != st.a[i][j]) ret++; return ret;}struct node { matrix a; int t; bool operator<(node x) const { return t + h(a) > x.t + h(x.a); }} x;priority_queue
q;set
s;int main() { st.a[1][1] = 1; st.a[1][2] = 2; st.a[1][3] = 3; st.a[2][1] = 8; st.a[2][2] = 0; st.a[2][3] = 4; st.a[3][1] = 7; st.a[3][2] = 6; st.a[3][3] = 5; for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) { scanf(" %c", &ch); f.a[i][j] = ch - '0'; } q.push({f, 0}); while (!q.empty()) { x = q.top(); q.pop(); if (!h(x.a)) { printf("%d\n", x.t); return 0; } for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) if (!x.a.a[i][j]) fx = i, fy = j; for (int i = 0; i < 4; i++) { int xx = fx + dx[i], yy = fy + dy[i]; if (1 <= xx && xx <= 3 && 1 <= yy && yy <= 3) { swap(x.a.a[fx][fy], x.a.a[xx][yy]); if (!s.count(x.a)) s.insert(x.a), q.push({x.a, x.t + 1}); swap(x.a.a[fx][fy], x.a.a[xx][yy]); } } } return 0;}

继续想想还有没有其他方法,

假设每一步都是有意义的,那么一个数字成功对上也要移动他们的曼哈顿距离次

所以,我们将估价函数设计为所有数字与目标状态中数字的曼哈顿距离之和(当然0不算,否则你就可以高兴的WA了)

还有一个显然的优化,就是记录上一次的操作

而且我们不一定要用二维数组,可以用一个string来保存状态。对于一个string中的下标i,纵坐标为i/3, 横坐标为i%3。对于二维数组下标x, y, string中的下标便为x * 3 + y

还有不明白的,请看代码

IDA_star

除了估价函数,还有一个显然的优化是记录上一次的操作

#include
using namespace std;string st, ed;//起始状态和目标 int depth;//搜索深度 //估价函数,为每个数字的曼哈顿距离之和 int hfunc(string st) { int ans = 0; for (register int i = 0; i < st.size(); ++i) { if (st[i] == '0') continue;//0不算,否则会WA int j = ed.find(st[i]), r = i / 3, c = i % 3; int x = j / 3, y = j % 3; //横坐标为/3, 纵坐标为%3 ans += abs(r - x) + abs(c - y); } return ans;}const int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, -1, 1};//IDA_star//除了估价函数,还有一个显然的优化是记录上一次的操作 bool dfs(int now, int pre) { int cnt = hfunc(st); if (!cnt) return 1; if (now + cnt > depth) return 0; //当前步数+估价>深度限制,立即回溯 int pos = st.find('0'), x = pos / 3, y = pos % 3; for (register int i = 0; i < 4; ++i) { int nx = x + dx[i], ny = y + dy[i]; if (nx < 0 || nx > 2 || ny < 0 || ny > 2 || nx * 3 + ny == pre) continue; //数组中的下标为横坐标*3+纵坐标 swap(st[pos], st[nx * 3 + ny]); if (dfs(now + 1, pos)) return 1; swap(st[pos], st[nx * 3 + ny]); } return 0;}int main() { cin >> st; ed = "123804765"; depth = hfunc(st); while (depth <= 27 && !dfs(0, -1)) ++depth; cout << depth << endl;}

从AC速度上看 IDA_star 利用了保存的结果明显加快了速度

同样这道题也可以使用BFS做,毕竟当估值函数 h = 1时就是BFS嘛 ^ v ^

转载地址:http://uuykz.baihongyu.com/

你可能感兴趣的文章
一些留给自己的思考题(只求回过头来能够有所获)
查看>>
delete对象时会自动调用类的析构函数
查看>>
C++ 子类对象直接赋值给父类对象可行,反过来不行
查看>>
linux下同一个动态库名为何辣么多的.so文件
查看>>
SQL联表的方式(逗号, Left Join, Right Join)
查看>>
牛客网输入输出举例
查看>>
字符串初始化时的注意点
查看>>
软考相关试题
查看>>
顺序表的操作
查看>>
POD类型
查看>>
const与常量,傻傻分不清楚~
查看>>
Head First设计模式——迭代器模式
查看>>
MongoDB版本及存储引擎区别
查看>>
shell echo单行和多行文字定向写入到文件中
查看>>
AtCoder Beginner Contest 100 题解
查看>>
Java高性能编程之CAS与ABA及解决方法
查看>>
《算法导论》第二章笔记
查看>>
HTML节点操作
查看>>
HTML5新特性
查看>>
cmp命令
查看>>