博客园同步

原题链接

简要题意:

给定一个 3 × 3 3 \times 3 3×3 的矩阵,每次可以把空格旁边(四方向)的一个位置移到空格上。求到目标状态的最小步数。

前置知识:

单向宽搜的写法

OK \text{OK} OK,现在我们来考虑双向宽搜。

假设 A A A 和 B B B 两个人被困在了迷宫的两个角落,现在他们首先要互相找到对方;他们都会分身术。你认为下面哪一种方法最快:

  • A A A 主动分身去各个路口分支找 B B B, B B B 原地待命。

  • B B B 主动分身去各个路口分支找 A A A, A A A 原地待命。

  • A A A 和 B B B 同时分身去各个路口分支找对方。

无可厚非是最后一种方法最快。但请不要误解:现实生活中我们提倡前两种方案,因为现实中没有人会分身的

诚然,互相找的效率是最高的。可是你可能会问了:

假设一共 4 4 4 步找到对方,两人各走 2 2 2 步和一个人找 4 4 4 步不是一样的吗?

粗想一下,确实如此。但是在 爆炸性指数级的压力 之下,完全不同。

就在这个问题的基础上,假设每走 1 1 1 步都有 4 4 4 种选法(即四方向)。

那么,一个人找的时间是 4 4 = 256 4^4 = 256 44=256.

两个人同时找对方的时间是 4 2 + 4 2 = 32 4^2 + 4^2 = 32 42+42=32.

数据说明,快了 8 8 8 倍。这单是 4 4 4 步就快了 8 8 8 倍!

经过粗略的计算,假设一共要走 20 20 20 步的话,双向找比单向快 524288 524288 524288 倍,约 5.2 × 1 0 5 5.2 \times 10^5 5.2×105,假设时间限制是 1 s 1s 1s 的话,显然这两个程序的分数是有着极大差异的!

这是因为,双向搜索在本质上把步数减半了,而 在指数上减半会让幂大大降低,因此双向搜索会更快。

那么双向搜索适用于哪些题目呢?

  • 明确知道起点和终点的。比方说这种题(现编的):
对于已知的一个数,每次可以将其连续 k 个数字同时 +1.
求让它至少有 p 位连续相同的步数。

显然,终点不明确,无法搜索。你不可能把所有的终点都枚举一遍。

  • 明确知道搜索深度的。即明确知道多少步会走到。比方说 埃及分数:
将一个分数分解为若干分数的和。有一些分母不能使用。

显然,你不知道会分解成多少个分数。因此本题需要使用 迭代加深搜索(IDDFS) 而非 bfs \text{bfs} bfs.

然后,那你会问了:八数码这一题,我也不知道最多会有多少步呀?

  • 那么,你不会自己随机造吗?

  • 我怎么造啊?

  • 用随机种子搞一个 0 0 0 ~ 8 8 8 的任意排列,然后取出最大答案啊

  • 我连 std \text{std} std 都不会写啊

哦!对。本题你可以稍微分析一下,你会发现,既然肯定能走到,你的直觉:一定不超过 30 30 30 步。(事实如此)

双向宽搜如何实现呢?

  • 将起点和终点一起入队,用 vis \text{vis} vis 记录是否访问过。起点拓展的状态 v i s = 1 vis = 1 vis=1,终点拓展的状态 v i s = 2 vis = 2 vis=2,否则 v i s = 0 vis = 0 vis=0. 并用 a n s ans ans 记录当前的步数。

  • 对当前状态 u u u,转为矩阵并进行四方向的转移,形成新的状态 v v v。

  • 若当前状态已搜过,分情况:

    • v i s u = v i s v vis_u = vis_v visu​=visv​,直接跳过
    • v i s u + v i s v = 3 vis_u + vis_v = 3 visu​+visv​=3,输出 a n s u + a n s v ans_u + ans_v ansu​+ansv​,停止搜索
    • v i s v ≠ 0 vis_v \not = 0 visv​​=0,则 v i s v ← v i s u , a n s v ← a n s u + 1 vis_v \gets vis_u , ans_v \gets ans_u+1 visv​←visu​,ansv​←ansu​+1,入队,继续搜索。
  • 接着入队,进行下一轮搜索。

  • 重复搜索直到队列为空(当然本题保证不会无解,因此队列不会为空,但严谨地说明一下)或已有答案。

你会注意到 v i s vis vis 和 a n s ans ans 都需要用 map \text{map} map,这无疑让我们多挂了两个 log ⁡ \log log. 假设队列的一个 log ⁡ \log log,一共有 3 3 3 个 log ⁡ \log log.(针对入队的状态个数出现了 3 3 3 个 log ⁡ \log log) 每次取出状态需要变为矩阵, × 9 \times 9 ×9.

那么假设一共搜到状态个数是 n n n,那么:

时间复杂度: O ( 9 n log ⁡ 3 n ) \mathcal{O}(9n \log^3 n) O(9nlog3n).

这是理论上的硬性分析。这能用最慢点 10 m s 10ms 10ms, 31 31 31 个点共 149 m s 149ms 149ms 的优秀时间通过,也就说明, n n n 是 1 0 3 10^3 103 级别的。

如果你不信,代入 n = 1 0 4 n=10^4 n=104,可得:

9 × 1 0 4 × 1 4 3 = 90000 × 2744 = 246960000 ‬ 9 \times 10^4 \times 14^3 = 90000 \times 2744 = 246960000‬ 9×104×143=90000×2744=246960000‬

大概是 2.4 × 1 0 8 2.4 \times 10^8 2.4×108 !你觉得这 10 m s 10ms 10ms 能跑完?

要是能跑完,那洛谷评测机早成 神威 · 太湖之光了, 1 s 1s 1s 那就能跑 2.4 × 1 0 10 2.4 \times 10^{10} 2.4×1010 了,那还不乱套了, O ( n 2 ) \mathcal{O}(n^2) O(n2) 都可以稳过 1 0 5 10^5 105 了!

然而实际,时间复杂度: O ( wys ) \mathcal{O}(\text{wys}) O(wys).

实际得分: 100 p t s 100pts 100pts.

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}inline void write(int x) {if(x<0) {putchar('-');write(-x);return;}if(x<10) {putchar(char(x%10+'0'));return;}write(x/10);putchar(char(x%10+'0'));
}int n,end=123804765,a[4][4];
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0};
queue<int> q;
map<int,int> vis,ans;inline void bfs() {if(n==end) {puts("0");return;}q.push(n); q.push(end);ans[n]=0; ans[end]=1;vis[n]=1; vis[end]=2; //初始化while(!q.empty()) {int now=q.front(),fx,fy; q.pop();int t=now; /*printf("%d\n",now);*/for(int i=3;i>=1;i--) for(int j=3;j>=1;j--) {a[i][j]=now%10,now/=10;if(!a[i][j]) fx=i,fy=j; //转化为矩阵} for(int i=0;i<4;i++) {int nx=fx+dx[i],ny=fy+dy[i];if(nx<1 || nx>3 || ny<1 || ny>3) continue;swap(a[fx][fy],a[nx][ny]); now=0;for(int j=1;j<=3;j++)for(int k=1;k<=3;k++) now=now*10+a[j][k]; //再转回来if(vis[now]==vis[t]) { //搜过swap(a[fx][fy],a[nx][ny]); //换回来continue;} if(vis[now]+vis[t]==3) {printf("%d\n",ans[t]+ans[now]); //记录答案return;} ans[now]=ans[t]+1; vis[now]=vis[t];q.push(now); swap(a[fx][fy],a[nx][ny]); //入队,记录,换回}}
}int main() {n=read(); bfs();return 0;
}

P1379 八数码难题 题解(双向宽搜)相关推荐

  1. 洛谷—P1379 八数码难题

    题目链接:P1379 八数码难题 题目大意: 要求最少步骤的移动方法,实现从初始布局到目标布局的转变. 解题思路: 这道题目要用到搜索中比较难的搜索方法-迭代加深的A*算法.所谓迭代加深就是每次限制搜 ...

  2. 习题:八数码难题(双向BFS)

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

  3. 洛谷P1379八数码难题

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

  4. [洛谷] P1379 八数码难题( 提高+/省选- )

    八数码 1.题目 2.分析 3.代码 1. bfs (+queue) + unordered_map 重点分析 2.双向bfs (适用于知道起始状态的情况) 思路分析 3.双向bfs优化 思路 4.总 ...

  5. 洛谷OJ:P1379 八数码难题(双向搜索)

    思路:相信不少小伙伴上来就是暴力DFS,但是拿到题之后我们不妨想一想如果纯DFS爆搜下来会产生多少种状态,这样的方法是否是最优的? 这里选择使用一种称之为双向搜索的方法(通过知乎学到的,放出来shar ...

  6. 【洛谷】P1379 八数码难题

    题目地址: https://www.luogu.com.cn/problem/P1379 题目描述: 在3×33×33×3的棋盘上,摆有八个棋子,每个棋子上标有111至888的某一数字.棋盘中留有一个 ...

  7. IDA*-洛谷P1379 八数码难题

    https://daniu.luogu.org/problem/show?pid=1379 省选的收获 暗金 学会了A*啦啦啦: 我在第一天学了A*; 然后回家颓废之余思考思考: 又问了van爷一些小 ...

  8. 【基础练习】【BFS+A*】codevs1225八数码难题题解

    题目描写叙述 Description Yours和zero在研究A*启示式算法.拿到一道经典的A*问题,可是他们不会做,请你帮他们. 问题描写叙述 在3×3的棋盘上,摆有八个棋子,每一个棋子上标有1至 ...

  9. 【codevs1225】八数码难题,如何精确地搜索

    1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description Yours和zero在研究A*启发式算法.拿到一道 ...

最新文章

  1. 为什么ConcurrentHashMap的读操作不需要加锁?
  2. java 自然语言处理_Java自然语言处理
  3. 桌面虚拟化“寻人行动”-转裁
  4. 简单有效提升服务器性能,4个简单操作,让你的服务器性能飞速提升!
  5. webpack那些事儿
  6. PHP中Foreach在引用时的陷阱
  7. Firefox和Chrome浏览器导出书签
  8. pytorch cpu版本安装_小白学pytorch 01-安装C++/Python版本pytorch
  9. TCP/IP报文详解
  10. 最近发现的一个c# winform的一个很好用很强大的excel控件 reogrid控件
  11. Sloth组件之NetRisc.Configuration源代码发布
  12. 亚马逊云服务器防火墙,Amazon WAF | Web 应用程序防火墙 | 亚马逊云科技
  13. 数据分析之EXCL筛选多列相同的数据(满足多列条件的数据)进行处理
  14. 手把手介绍Manjaro中添加Google输入法---亲身测试,成功安装
  15. 大一作业HTML期末网页作业(化妆品网站制作) html+css+js 雅诗兰黛美妆网站制作
  16. [Android Studio] 手机安装apk报错解决方法
  17. Android 打包解析软件包时出现问题
  18. 合肥python儿童编程_合肥少儿编程学习中心
  19. 美国亚马逊图片打不开
  20. 深度清洁,戴森Cyclone V10无绳吸尘器的高端新味道

热门文章

  1. Linux下Jboss安装使用+Jboss部署脚本
  2. ffmpeg 去水印,使用GPU让效率提升5倍
  3. 杭州计算机考研培训班,杭州有名的计算机考研培训班学费多少
  4. 数据结构实验报告3————栈和队列及其应用
  5. 新零售mysql设计(品牌表 分类表 品牌分类表)(二次修改)
  6. 解决SecureCRT串口日志突然出现乱码的问题
  7. data:text/html html contenteditable,HTML5它contenteditable属性
  8. Linux 磁带机备份完全攻略
  9. 软件设计是怎样炼成的(7)——细节决定成败(详细设计)
  10. 【CSDN AI 周刊】第9期 | TensorFlow 1.0 官方正式发布