题意:w*h(w,h16)网格上有n(n3)个小写字母(代表鬼)。要求把它们分别移动到对应的大写字母里。每步可以有多个鬼同时移动(均为往上下左右四个方向之一移动),但每步结束之后任何两个鬼不能占用同一个位置,也不能在一步之内交换位置。

先来第一种单向bfs,需要注意的是如果像平常bfs判断的话,一定会超时,因为每个节点每次有5种移动方式,3个节点,最多有种情况,但是可以将空地拿出来,变成图。题目中有一个非常好用的条件——任何一个2*2子网格中至少有一个障碍格,最多16*16个格子,每四个有一个障碍物,也就是256*(3/4)=192,所以空地有最多192个,判重的话就用200*200*200=8000000数组来存,另外图是将二维坐标映射成一维数组。开始时候每次找到一个空地那么空地数量加一,也就是坐标映射值。一开始竟然没有想到。。

#include <cstdio>
#include <cstring>
#include <cctype>
#include <list>
#include <algorithm>using namespace std;int w, h, n;char pic[20][20]; // 输入
int num[20][20]; // 输入中的位置→图中节点的编号
int vis[200][200][200]; // 标记数组
int connect[200][200]; // 邻接表
int all; // 图中节点的数量int que[10000000][4]; // BFS队列
int goal[4]; // 目标状态inline void BFS() {// 初始化memset(vis, 0, sizeof(vis));int fro = 0, rear = 1;vis[que[0][1]][que[0][2]][que[0][3]] = true;while(fro < rear) {int &step = que[fro][0], &a = que[fro][1], &b = que[fro][2], &c = que[fro][3];if(a == goal[1] && b == goal[2] && c == goal[3]) { goal[0] = step; return; }for(int i = 0, t1; i <= connect[a][0]; ++i) {t1 = (i == 0 ? a : connect[a][i]);for(int j = 0, t2; j <= connect[b][0]; ++j) {t2 = (j == 0 ? b : connect[b][j]);for(int k = 0, t3; k <= connect[c][0]; ++k) {t3 = (k == 0 ? c : connect[c][k]);// 判断冲突-----if((t1 && t2 && t1 == t2) || (t1 && t3 && t1 == t3) || (t2 && t3 && t2 == t3)) continue; // 不能重合if(t1 && t2 && t1 == b && t2 == a) continue; // t1,t2不能对穿if(t1 && t3 && t1 == c && t3 == a) continue; // t1,t3不能对穿if(t2 && t3 && t2 == c && t3 == b) continue; // t2,t3不能对穿// ----------if(!vis[t1][t2][t3]) {vis[t1][t2][t3] = 1;que[rear][0] = step + 1, que[rear][1] = t1, que[rear][2] = t2, que[rear][3] = t3;++rear;}}}}++fro;}
}int main() {int _t = 0;while(scanf("%d%d%d\n", &w, &h, &n) && w && h && n) {// 读取输入-----for(int i = 0; i != h; ++i) fgets(pic[i],20,stdin);// ----------// 根据输入建立图-----// 初始化memset(connect, 0, sizeof(connect));all = 0;// 获得编号for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) {if(pic[i][j] != '#') num[i][j] = ++all;else num[i][j] = 0;}// 建立图for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(num[i][j]) {int &pos = num[i][j];if(num[i + 1][j]) connect[pos][++connect[pos][0]] = num[i + 1][j];if(num[i - 1][j]) connect[pos][++connect[pos][0]] = num[i - 1][j];if(num[i][j + 1]) connect[pos][++connect[pos][0]] = num[i][j + 1];if(num[i][j - 1]) connect[pos][++connect[pos][0]] = num[i][j - 1];}// ----------// 寻找初始状态和目标状态(测了一下字母范围只在abc之间所以偷懒就这么写了)//初始化que[0][0] = que[0][1] = que[0][2] = que[0][3] = 0;goal[0] = goal[1] = goal[2] = goal[3] = 0;// 寻找初始状态for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(islower(pic[i][j])) {if(pic[i][j] == 'a') que[0][1] = num[i][j];if(pic[i][j] == 'b') que[0][2] = num[i][j];if(pic[i][j] == 'c') que[0][3] = num[i][j];}// 寻找目标状态for(int i = 0; i != h; ++i) for(int j = 0; j != w; ++j) if(isupper(pic[i][j])) {if(pic[i][j] == 'A') goal[1] = num[i][j];if(pic[i][j] == 'B') goal[2] = num[i][j];if(pic[i][j] == 'C') goal[3] = num[i][j];}// ----------BFS();printf("%d\n", goal[0]);}
}

正常思路是这个但是超时,下面看一下作者写的单向bfs,又是二进制。。

区别就是他使用了编码队列

#include<cstdio>#include<cstring>#include<cctype>#include<queue>using namespace std;const int maxs = 20;const int maxn = 150; // 75% cells plus 2 fake nodesconst int dx[]={1,-1,0,0,0}; // 4 moves, plus "no move"const int dy[]={0,0,1,-1,0};inline int ID(int a, int b, int c) {return (a<<16)|(b<<8)|c;}int s[3], t[3]; // starting/ending position of each ghostint deg[maxn], G[maxn][5]; // target cells for each move (including "no move")inline bool conflict(int a, int b, int a2, int b2) {return a2 == b2 || (a2 == b && b2 == a);}int d[maxn][maxn][maxn]; // distance from starting stateint bfs() {queue<int> q;memset(d, -1, sizeof(d));q.push(ID(s[0], s[1], s[2])); // starting noded[s[0]][s[1]][s[2]] = 0;while(!q.empty()) {int u = q.front(); q.pop();int a = (u>>16)&0xff, b = (u>>8)&0xff, c = u&0xff;if(a == t[0] && b == t[1] && c == t[2]) return d[a][b][c]; // solution foundfor(int i = 0; i < deg[a]; i++) {int a2 = G[a][i];for(int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if(conflict(a, b, a2, b2)) continue;for(int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if(conflict(a, c, a2, c2)) continue;if(conflict(b, c, b2, c2)) continue;if(d[a2][b2][c2] != -1) continue;d[a2][b2][c2] = d[a][b][c]+1;q.push(ID(a2, b2, c2));}}}}return -1;}int main() {int w, h, n; while(scanf("%d%d%d\n", &w, &h, &n) == 3 && n) {char maze[20][20];for(int i = 0; i < h; i++)fgets(maze[i], 20, stdin);// extract empty cellsint cnt, x[maxn], y[maxn], id[maxs][maxs]; // cnt is the number of empty cellscnt = 0;for(int i = 0; i < h; i++)for(int j = 0; j < w; j++)if(maze[i][j] != '#') {x[cnt] = i; y[cnt] = j; id[i][j] = cnt;if(islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;else if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;cnt++;}// build a graph of empty cellsfor(int i = 0; i < cnt; i++) {deg[i] = 0;for(int dir = 0; dir < 5; dir++) {int nx = x[i]+dx[dir], ny = y[i]+dy[dir];// "Outermost cells of a map are walls" means we don't need to check out-of-boundif(maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];}}// add fakes nodes so that in each case we have 3 ghosts. this makes the code shorterif(n <= 2) { deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }if(n <= 1) { deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }printf("%d\n", bfs());}return 0;}

双向bfs:

就是终点和起点一起开始,看谁先到达对方经过的点。具体实现看代码。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
using namespace std;
const int maxs = 20;
const int maxn = 150; // 75% cells plus 2 fake nodes
const int dx[] = { 1,-1,0,0,0 }; // 4 moves, plus "no move"
const int dy[] = { 0,0,1,-1,0 };
inline int ID(int a, int b, int c) {return (a << 16) | (b << 8) | c;
}
int s[3], t[3]; // starting/ending position of each ghost
int deg[maxn], G[maxn][5]; // target cells for each move (including "no move")inline bool conflict(int a, int b, int a2, int b2) {return (a2 == b2) || (a2 == b && b2 == a);
}int d[maxn][maxn][maxn]; // distance from starting state
int vis[maxn][maxn][maxn];
int bfs() {queue<int> q;queue<int>p;memset(d, 0, sizeof(d));memset(vis, 0, sizeof(vis));q.push(ID(s[0], s[1], s[2])); // starting nodep.push(ID(t[0], t[1],t[2]));d[s[0]][s[1]][s[2]] = 0;d[t[0]][t[1]][t[2]] = 1;vis[s[0]][s[1]][s[2]] = 1;//将从起点经过的标记为1vis[t[0]][t[1]][t[2]] = 2;//从终点开始的标记为2while (!q.empty()||!q.empty()) {int c1 = q.size(); int c2 = p.size();while (c1--) {int u = q.front(); q.pop();int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;for (int i = 0; i < deg[a]; i++) {int a2 = G[a][i];for (int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if (conflict(a, b, a2, b2)) continue;for (int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if (conflict(a, c, a2, c2)) continue;if (conflict(b, c, b2, c2)) continue;if (!vis[a2][b2][c2]) {vis[a2][b2][c2] = 1;//d[a2][b2][c2] = d[a][b][c] + 1;q.push(ID(a2, b2, c2));}else if (vis[a2][b2][c2] == 2) {//走到终点经过的节点return d[a][b][c] + d[a2][b2][c2];//返回起点走过的路程加上从终点走过的路程}}}}}while (c2--) {int u = p.front(); p.pop();int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;for (int i = 0; i < deg[a]; i++) {int a2 = G[a][i];for (int j = 0; j < deg[b]; j++) {int b2 = G[b][j];if (conflict(a, b, a2, b2)) continue;for (int k = 0; k < deg[c]; k++) {int c2 = G[c][k];if (conflict(a, c, a2, c2)) continue;if (conflict(b, c, b2, c2)) continue;if (!vis[a2][b2][c2]) {vis[a2][b2][c2] = 2;//d[a2][b2][c2] = d[a][b][c] + 1;p.push(ID(a2, b2, c2));}else if (vis[a2][b2][c2] == 1) {//走到起点经过的节点return d[a][b][c] + d[a2][b2][c2];//返回起点走过的路程加上从终点走过的路程}}}}}}return -1;}int main() {int w, h, n;while (scanf("%d%d%d\n", &w, &h, &n) == 3 && n) {char maze[20][20];for (int i = 0; i < h; i++)fgets(maze[i], 20, stdin);// extract empty cellsint cnt, x[maxn], y[maxn], id[maxs][maxs]; // cnt is the number of empty cellscnt = 0;for (int i = 0; i < h; i++)for (int j = 0; j < w; j++)if (maze[i][j] != '#') {x[cnt] = i; y[cnt] = j; id[i][j] = cnt;if (islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;else if (isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;cnt++;}// build a graph of empty cellsfor (int i = 0; i < cnt; i++) {deg[i] = 0;for (int dir = 0; dir < 5; dir++) {int nx = x[i] + dx[dir], ny = y[i] + dy[dir];// "Outermost cells of a map are walls" means we don't need to check out-of-boundif (maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];}}// add fakes nodes so that in each case we have 3 ghosts. this makes the code shorterif (n <= 2) { deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }if (n <= 1) { deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }printf("%d\n", bfs());}return 0;
}

时间从850ms变成470ms了

UVA1601The Morning after Halloween 单向加双向bfs相关推荐

  1. 搜索算法——双向bfs

    双向bfs适用于知道起点和终点的状态下使用,从起点和终点两个方向开始进行搜索,可以非常大的提高单个bfs的搜索效率 同样,实现也是通过队列的方式,可以设置两个队列,一个队列保存从起点开始搜索的状态,另 ...

  2. HDU 1043 Eight(双向BFS+康托展开)

    http://acm.hdu.edu.cn/showproblem.php?pid=1043 题意:给出一个八数码,求出到达指定状态的路径. 思路:路径寻找问题.在这道题里用到的知识点挺多的.第一次用 ...

  3. HDU - 3085 Nightmare Ⅱ(双向bfs)

    题目链接:点击查看 题目大意:给出一个迷宫,一个男孩和一个女孩还有两只鬼,男孩每秒钟走3格,女孩每秒钟走1格,鬼每秒钟向四周分裂2格,问男孩和女孩能否在鬼占领迷宫之前汇合,能的话输出汇合时间,否则输出 ...

  4. WCF简单教程(6) 单向与双向通讯

    第六篇:单向与双向通讯 项目开发中我们时常会遇到需要异步调用的问题,有时忽略服务端的返回值,有时希望服务端在需要的时候回调,今天就来看看在WCF中如何实现. 先看不需要服务端返回值的单向调用,老规矩, ...

  5. LeetCode 127. 单词接龙(图的BFS/双向BFS)

    文章目录 1. 题目 2. 图的BFS解题 2.1 单向BFS 2.2 双向BFS !厉害了 1. 题目 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord ...

  6. 地址总线是单向还是双向_如何区分晶闸管的单双向问题?

    可控硅又叫做晶闸管,是一种常用的半导体器件,是一种能像闸门一样控制电流大小的半导体器件.因此,可控硅也具有开关控制.电压调整和整流等功能.可控硅的种类较多,强电电路采用的可控硅主要有单向晶闸管和双向晶 ...

  7. python 八数码_python 处理八数码 双向BFS 拼图游戏 | 学步园

    我的代码一开始是单向的BFS 然后很慢,5分钟? 参照别人的代码后,改用双向BFS 很快 拼图问题 == 八数码问题 从开始状态start,进行BFS 同时,从结束状态end,进行BFS 如果左图能够 ...

  8. 地址总线是单向还是双向_双向可控硅和单向可控硅的区别

    可控硅又叫做晶闸管,是一种常用的半导体器件,是一种能像闸门一样控制电流大小的半导体器件.因此,可控硅也具有开关控制.电压调整和整流等功能.可控硅的种类较多,强电电路采用的可控硅主要有单向晶闸管和双向晶 ...

  9. C++解题报告:详解经典搜索难题——八数码问题( 双向BFS A* 求解)

    引言 AC这道八数码问题,你和楼教主就是兄弟了... 题目描述 在一个3*3的九宫格棋盘里,放有8个数码,数码的数字分别是1~8.棋盘中还有一个位置是空着的,用0表示.可以通过在九宫格里平移数码来改变 ...

最新文章

  1. ceph osd混合部署和普通部署
  2. linux课程设计死锁避免,linux操作系统课程设计—车辆死锁.doc
  3. (1)java虚拟机概念和结构图
  4. jQuery教程(十三)jQuery Lightbox (插件)
  5. JavaScript放头部不执行的情况(针对新手)
  6. livecharts中仪表盘_LiveCharts文档-3开始-4可用的图表
  7. 腾讯35k招.NET Core开发,深扒这些技术要求 真的很难吗?
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的新闻头条管理系统
  9. CSS基础——CSS 列表和表单【学习笔记】
  10. Linux自学笔记——tcp wrapper
  11. CodeForces - 808B Average Sleep Time
  12. 相机sd卡格式化后还能不能数据再恢复
  13. 回想过去几年的编程生活
  14. C++ 解决经典哥尼斯堡七桥问题
  15. NP架构-汇聚层路由器完美选择
  16. [iOS]判断设备是否越狱
  17. uni-app基础知识
  18. camunda 流程执行追踪_Camunda流程调用梳理
  19. geoserver离线地图服务搭建和图层发布
  20. 常见的积分商城游戏类型有哪些?

热门文章

  1. 分布式ID-数据库多主模式
  2. Docker常用操作
  3. 当开启了延迟加载的开关,对象是怎么变成代理对象的?
  4. 通过@Value + @PropertySource来给组件赋值
  5. 反射获取空参数构造方法并运行
  6. 递归概念分类注意事项
  7. Bootstrap全局css样式_表单
  8. Docker容器的root用户
  9. 解决‘C:\Program‘ 不是内部或外部命令,也不是可运行的程序或批处理文件
  10. HX720/HX711 数据采集及处理姿态解析(公式及源码)