P1379 八数码难题

题目描述

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

输入格式

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

输出格式

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

输入输出样例

输入 #1

283104765

输出 #1

4

分析:

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

123
804
765

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

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

代码实现:

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <set>
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<node> q;
set<matrix> 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<bits/stdc++.h>
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 ^

P1379 八数码难题 ( A* 算法 与 IDA_star 算法)的更多相关文章

  1. 洛谷 P1379 八数码难题 解题报告

    P1379 八数码难题 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初 ...

  2. 洛谷——P1379 八数码难题

    P1379 八数码难题 双向BFS 原来双向BFS是这样的:终止状态与起始状态同时入队,进行搜索,只不过状态标记不一样而已,本题状态使用map来存储 #include<iostream> ...

  3. [luogu]P1379 八数码难题[广度优先搜索]

    八数码难题 ——!x^n+y^n=z^n 我在此只说明此题的一种用BFS的方法,因为本人也是初学,勉勉强强写了一个单向的BFS,据说最快的是IDA*(然而蒟蒻我不会…) 各位如果想用IDA*的可以看看 ...

  4. 洛谷P1379八数码难题

    题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中. 要求解的问题是:给出一种初始布局(初始状态)和目标布局(为 ...

  5. luogu P1379 八数码难题(A*算法入门详细讲解)

     代码实现细节 #include<cstdio> #include<cstring> #include<iostream> using namespace std; ...

  6. 洛谷 P1379 八数码难题 Label:判重&&bfs

    特别声明:紫书上抄来的代码,详见P198 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给 ...

  7. 【洛谷】P1379 八数码难题(bfs)

    题目 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局 ...

  8. 洛谷 P1379 八数码难题

    题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了 ...

  9. 洛谷 - P1379 - 八数码难题 - bfs

    https://www.luogu.org/problemnew/show/P1379 #include <bits/stdc++.h> using namespace std; #def ...

  10. 洛谷—— P1379 八数码难题

    https://daniu.luogu.org/problem/show?pid=1379 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示 ...

随机推荐

  1. 用友NCC产品API使用指南

    轻易云用友NCC产品API集成专题 open api简介 Open API即开放API,也称开放平台. 所谓的开放API(OpenAPI)是服务型网站常见的一种应用,网站的服务商将自己的网站服务封装成 ...

  2. ssm整合-异常处理器

    异常处理器   程序开发过程中不可避免会遇到异常现象 类似于这样的异常 异常出现的种类: 各个层均可能出现异常,当我们出现异常时,处理代码应该写在哪一层?   表现层,因为要把异常网上抛,在表现层进行 ...

  3. 9、For语句

    1.for循环概念 程序的流程控制结构一共有三种: 顺序结构,选择结构,循环结构. 顺序结构:代码从上向下逐行的执行 选择结构:条件满足,某些代码才会被执行.0-1次 if语句,switch语句 循环 ...

  4. CSP_J

  5. 关于`dial unix /var/run/docker.sock: connect: permission denied`的处理方法笔记

    之前遇到的一个问题,使用非root用户时操作docker提示无权限,在查阅了一些文章之后自己又摸索出了一些更方便的方法,顺手记录下来. 一.问题发现 根据报错信息dial unix /var/run/ ...

  6. Hugging Face 年度回顾:2023,开源大模型之年

    在 2023 年,大型语言模型(Large Language Models,简称 LLMs)受到了公众的广泛关注,许多人对这些模型的本质及其功能有了基本的了解.是否开源的议题同样引起了广泛的讨论.在 ...

  7. MinIO客户端之mv

    MinIO提供了一个命令行程序mc用于协助用户完成日常的维护.管理类工作. 官方资料 mc mv 将对象在桶之间移动. 在桶内准备好待移动的文件,检查对象,命令如下: ./mc ls local1/b ...

  8. 【scikit-learn基础】--『监督学习』之 决策树回归

    决策树算法是一种既可以用于分类,也可以用于回归的算法. 决策树回归是通过对输入特征的不断划分来建立一棵决策树,每一步划分都基于当前数据集的最优划分特征.它的目标是最小化总体误差或最大化预测精度,其构建 ...

  9. Pikachu漏洞靶场 Sql Inject(SQL注入)

    SQLi 哦,SQL注入漏洞,可怕的漏洞. 文章目录 SQLi 数字型注入(post) 字符型注入(get) 搜索型注入 xx型注入 "insert/update"注入 inser ...

  10. pyecharts 保存图表至本地

    pip install snapshot-selenium from pyecharts.render import make_snapshot from snapshot_selenium impo ...