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. C语言已知单链表LA=(a1,a2,…,am)和LB=(b1,b2,…,bn),编写程序按以下规则将它们合并成一个单链表LC,

    LC=(a1,b1,-,am,bm,bm+1,-,bn),m<=n 或者 LC=(a1,b1,-,bn,an,an+1,-,am),m>n /* 开发者:慢蜗牛 开发时间:2020.6.1 ...

  2. C语言编写一个程序,从键盘读入一个矩形的两个边的值(整数),输出矩形面积。

    #include<stdio.h> int main() { int i, j, s;//定义矩形的长,宽,面积变量 scanf_s("%d,%d", &i, ...

  3. Jenkins从Ubuntu迁移至AlmaLinux问题及相关解决记录

    相关背景 之前在Ubuntu平台上搭建了Jenkins(在Ubuntu机器上使用war包安装Jenkins),现在由于一些需求,需要将系统迁移到AlmaLinux平台.由于AlmaLinux属于Cen ...

  4. 【Servlet】两种配置

    web.xml中Servlet的注解 <servlet> <!-- servlet的内部名称,⾃定义 --> <servlet-name>类名</servle ...

  5. C#中HttpWebQuest发起HTTP请求,如何设置才能达到最大并发和性能

    在C#中使用HttpWebRequest发起HTTP请求时,达到最大并发和性能可以从以下几个方面改进: 1. ServicePointManager设置 ServicePointManager 类是一 ...

  6. 从零开始的 dbt 入门教程 (dbt-core 基础篇)

    最近一直在处理数据分析和数据建模的事情,所以接触了 dbt 等数据分析的工具,国内目前对于 dbt 比较详细的资料不多,所以打算写四道五篇 dbt 相关的文章,本文属于 dbt 系列的第一篇,本篇主要 ...

  7. Golang中如何自定义时间类型进行xml、json的序列化/反序列化

    在日常开发工作中,我们进行会遇到将 struct 序列化 json字符串以及将 json字符串 反序列化为 struct 的场景,大家也对此十分熟悉. 最近工作中,遇到了需要将 struct 序列化 ...

  8. jdk11的HttpClient

    我们都知道在jdk11之前都在用okhttp或者org.apache.httpcomponents  其实早在jdk9的时候这个方案就在孵化中 上面的截图来自openjdk的官网,注:openjdk是 ...

  9. MySQL笔记01: MySQL入门_1.1 MySQL概述

    1.1 MySQL概述 MySQL是一个关系数据库管理系统(Relational DataBase Management System,RDBMS).它是一个程序,可以存储大量的种类繁多的数据,并且提 ...

  10. Vue入门(1)安装vue环境,创建Vue2 Vue3的项目并且安装IIS环境和发布到IIS

    Vue环境的搭建 一.     背景 vue的运行方式有两种 一种是在页面引用vue的js包, 一种是搭建脚手架来vue框架. 我们在这里使用的是第二种方式. 需要安装的软件 npm 16.13.1 ...