UVA - 1601 The Morning after Halloween (双向BFS&单向BFS)
题目:
w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼)。要求把它们分别移动到对应的大写字母里。每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占用同一个位置,也不能在一步之内交换位置。输入保证所有空格连通,所有障碍格也连通,且任何一个2*2子网格中至少有一个障碍格。输出最少的步数。
分析:
1.又碰到了“状态”这个很玄乎的词了。以当前三个鬼的位置为状态,那就有256^3(16*16=256)个,然后三个鬼在每一个状态中的移动有5^3个,这时间和空间肯定受不了啊。
2.紫书上说,可以将空格提取出来,然后对其BFS就可以了……读完有些懵逼提取?那还不是遍历?看完代码才明白可以对每个空格子进行编号,然后存一下每个格子可以走向的格子的编号就提取出来了!!其中还对每一个状态进行了编号,这也太妙了!!还是太菜了……之后BFS就可以了。
单向BFS代码:
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define MAX 1000000009
#define FRE() freopen("in.txt","r",stdin)
#define FRO() freopen("out.txt","w",stdout)
using namespace std;
typedef long long ll;
const int maxn = ;
const int dx[] = {-,,,,};
const int dy[] = {,,-,,};
inline int ID(int a,int b,int c) {
return (a<<)|(b<<)|c;
}
int s[],t[];//保存初末状态
int deg[maxn];//某个格子有多少个与之相连的格子
int G[maxn][];//保存某个格子可以去向哪些格子
inline bool conflict(int a,int b,int a2,int b2) {
return a2==b2||(a2==b&&b2==a);//如果两个鬼移动到同一个位置或位置互换则冲突
}
int d[maxn][maxn][maxn];//走到某个状态经过的步数 int bfs(){
queue<int>q;
memset(d,-,sizeof(d));
q.push(ID(s[],s[],s[]));
d[s[]][s[]][s[]] = ;
while(!q.empty()){
int u=q.front();q.pop();
int a=(u>>)&0xff,b=(u>>)&0xff,c=u&0xff;//获取该状态下,三个鬼的位置 if(a==t[]&&b==t[]&&c==t[])//如果已经找到了,就返回结果
return d[a][b][c];
for(int i=; i<deg[a]; i++){//在一步中三个鬼到底该怎么走,那就三重循环枚举所有的可能情况就可以了
int a2=G[a][i];
for(int j=; j<deg[b]; j++){
int b2=G[b][j];
if(conflict(a,b,a2,b2))continue;
for(int k=; 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]!=-) continue;
d[a2][b2][c2] = d[a][b][c]+;//记录距离
q.push(ID(a2,b2,c2));
}
}
}
}
return -;
} int main() {
//FRE();
int w,h,n;
while(scanf("%d%d%d\n",&w,&h,&n)== && n){
char maze[][];
for(int i=; i<h; i++){
fgets(maze[i],,stdin);
}
int cnt,x[maxn],y[maxn],id[maxn][maxn];
cnt=;
for(int i=; i<h; i++){//提取图中所有可以走的格子
for(int j=; 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++;
}
}
}
for(int i=; i<cnt; i++){//遍历提取的格子
deg[i] = ;
for(int dir=; dir<; dir++){//记录可以从该格子走到的格子
int nx=x[i]+dx[dir],ny=y[i]+dy[dir];
if(maze[nx][ny]!='#')
G[i][deg[i]++] = id[nx][ny];
}
}
//如果起始格子不够3个就补全,让他们的起始位置和末位置重合就可以了
if(n==){
deg[cnt] = ; G[cnt][]=cnt;s[]=t[] = cnt++;
}
if(n==){
deg[cnt] = ; G[cnt][]=cnt;s[]=t[] = cnt++;
}
printf("%d\n",bfs());
}
return ;
}
这个题目也可以用双向BFS来做,做完这个题,对双向BFS有了进一步的理解。那这里的正向就是遍历小写字母,反向就是遍历大写字母。当小写字母和大写字母的位置重合之后,就是得到的答案。
双向BFS代码:
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define MAX 1000000009
#define FRE() freopen("in.txt","r",stdin)
#define FRO() freopen("out.txt","w",stdout)
using namespace std;
typedef long long ll;
const int maxn = ;
const int dx[] = {-, , , , };
const int dy[] = {, , -, , };
inline int ID(int a, int b, int c) {
return (a << ) | (b << ) | c;
}
int s[], t[]; //保存初末状态
int deg[maxn];//某个格子有多少个与之相连的格子
int G[maxn][];//保存某个格子可以去向哪些格子
inline bool conflict(int a, int b, int a2, int b2) {
return a2 == b2 || (a2 == b && b2 == a); //如果两个鬼移动到同一个位置或位置互换则冲突
}
int d[maxn][maxn][maxn], vis[maxn][maxn][maxn]; //走到某个状态经过的步数 int bfs() {
queue<int> que,que_rev;
d[s[]][s[]][s[]] = ;
d[t[]][t[]][t[]] = ; que.push(ID(s[],s[],s[]));
que_rev.push(ID(t[],t[],t[])); vis[s[]][s[]][s[]] = ;
vis[t[]][t[]][t[]] = ; while(!que.empty() && !que_rev.empty()){
int sza = que.size(),szb=que_rev.size();
while(sza--){
int u=que.front(); que.pop();
int a=(u>>)&0xff,b=(u>>)&0xff,c=(u&0xff);
for(int i=; i<deg[a]; i++){
int a2 = G[a][i];
for(int j=; j<deg[b]; j++){
int b2 = G[b][j];
if(conflict(a,b,a2,b2)) continue;
for(int k=; 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] = ;
d[a2][b2][c2] = d[a][b][c]+;
que.push(ID(a2,b2,c2));
}else if(vis[a2][b2][c2]==){
return d[a2][b2][c2]+d[a][b][c]+;
}
}
}
}
}
while(szb--){
int u=que_rev.front(); que_rev.pop();
int a=(u>>)&0xff, b=(u>>)&0xff,c=(u&0xff);
for(int i=; i<deg[a]; i++){
int a2 = G[a][i];
for(int j=; j<deg[b]; j++){
int b2=G[b][j];
if(conflict(a,b,a2,b2))continue;
for(int k=; 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]=;
d[a2][b2][c2] = d[a][b][c]+;
que_rev.push(ID(a2,b2,c2));
}else if(vis[a2][b2][c2]==){
return d[a2][b2][c2]+d[a][b][c]+;
}
}
}
}
}
}
return -;
} int main() {
//FRE();
int w, h, n;
while(scanf("%d%d%d\n", &w, &h, &n) == && n) {
char maze[][];
memset(d, -, sizeof(d));
memset(vis, , sizeof(vis));
int cnt, x[maxn], y[maxn], id[maxn][maxn];
cnt = ; for(int i = ; i < h; i++) {
fgets(maze[i], , stdin);
} for(int i = ; i < h; i++) { //提取图中所有可以走的格子
for(int j = ; 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++;
}
}
}
for(int i = ; i < cnt; i++) { //遍历提取的格子
deg[i] = ;
for(int dir = ; dir < ; dir++) { //记录可以从该格子走到的格子
int nx = x[i] + dx[dir], ny = y[i] + dy[dir];
if(maze[nx][ny] != '#')
{ G[i][deg[i]++] = id[nx][ny]; }
}
}
//如果起始格子不够3个就补全,让他们的起始位置和末位置重合就可以了
if(n == ) {
deg[cnt] = ;
G[cnt][] = cnt;
s[] = t[] = cnt++;
}
if(n == ) {
deg[cnt] = ;
G[cnt][] = cnt;
s[] = t[] = cnt++;
}
printf("%d\n", bfs());
}
return ;
}
UVA - 1601 The Morning after Halloween (双向BFS&单向BFS)的更多相关文章
- UVA - 1601 The Morning after Halloween (BFS/双向BFS/A*)
		
题目链接 挺有意思但是代码巨恶心的一道最短路搜索题. 因为图中的结点太多,应当首先考虑把隐式图转化成显式图,即对地图中可以相互连通的点之间连边,建立一个新图(由于每步不需要每个鬼都移动,所以每个点需要 ...
 - UVA 1601 The Morning after Halloween
		
题意: 给出一个最大为16×16的迷宫图和至多3个ghost的起始位置和目标位置,求最少经过几轮移动可以使三个ghost都到达目标位置.每轮移动中,每个ghost可以走一步,也可以原地不动,需要注意的 ...
 - UVa 1601 || POJ 3523 The Morning after Halloween (BFS || 双向BFS && 降维 && 状压)
		
题意 :w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼).要求把它们分别移动到对应的大写字母里.每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占 ...
 - <<操作,&0xff以及|的巧妙运用(以POJ3523---The Morning after Halloween(UVa 1601)为例)
		
<<表示左移,如a<<1表示将a的二进制左移一位,加一个0,&0xff表示取最后8个字节,如a&0xff表示取a表示的二进制中最后8个数字组成一个新的二进制数, ...
 - [uva P1601] The Morning after Halloween
		
[uva P1601] The Morning after Halloween 题目链接 非常经典的一道题目,lrj的书上也有(貌似是紫书?). 其实这题看起来就比较麻烦.. 首先要保证小鬼不能相遇, ...
 - POJ1915Knight Moves(单向BFS + 双向BFS)
		
题目链接 单向bfs就是水题 #include <iostream> #include <cstring> #include <cstdio> #include & ...
 - HDU1195 双向BFS(或BFS)
		
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1195 , 双向BFS或者直接BFS也可以过. 其实这道题只是单向BFS就可以过的,但是为了练算法,所以 ...
 - POJ 2251 Dungeon Master --- 三维BFS(用BFS求最短路)
		
POJ 2251 题目大意: 给出一三维空间的地牢,要求求出由字符'S'到字符'E'的最短路径,移动方向可以是上,下,左,右,前,后,六个方向,每移动一次就耗费一分钟,要求输出最快的走出时间.不同L层 ...
 - POJ1475 Pushing Boxes(BFS套BFS)
		
描述 Imagine you are standing inside a two-dimensional maze composed of square cells which may or may ...
 
随机推荐
- mysql自增主键在大量删除后如何重新设置避免断层
			
alter table tt auto_increment=8; 表tt mysql> select * from tt; +----+ | id | +----+ | 1 | | 2 | | ...
 - 7-74 JavaScript 事件
			
7-74 JavaScript 事件 学习要点 掌握常用的javaScript事件 基本概念 事件是一些特定动作发生时所发出的信号,JavaScript中的事件是可以被 JavaScript 侦测到的 ...
 - 123 c#调用oracle存储过程返回数据集  --通过oracle存储过程返回数据集
			
c#调用oracle存储过程返回数据集 2008-12-20 10:59:57| 分类: net|字号 订阅 CREATE OR REPLACE PACKAGE pkg_tableTypeIS ...
 - 深度学习——无监督,自动编码器——尽管自动编码器与 PCA 很相似,but自动编码器既能表征线性变换,也能表征非线性变换;而 PCA 只能执行线性变换
			
自动编码器是一种有三层的神经网络:输入层.隐藏层(编码层)和解码层.该网络的目的是重构其输入,使其隐藏层学习到该输入的良好表征. 自动编码器神经网络是一种无监督机器学习算法,其应用了反向传播,可将目标 ...
 - bzoj 1594: [Usaco2008 Jan]猜数游戏【二分+线段树】
			
写错一个符号多调一小时系列-- 二分答案,然后判断这个二分区间是否合法: 先按值从大到小排序,然后对于值相同的一些区间,如果没有交集则不合法:否则把并集在线段树上打上标记,然后值小于这个值的区间们,如 ...
 - bzoj 1232: [Usaco2008Nov]安慰奶牛cheer【最小生成树】
			
有趣 每条边在算答案的时候被算了二倍的边权值加上两个端点的权值,然后睡觉点额外加一次 所以可以用这个权做MST,然后加上点权最小的点 #include<iostream> #include ...
 - webapp填坑记录
			
网上也有许多的 webapp 填坑记录了,这几个月,我在公司正好也做了2个,碰到了一些问题,所以我在这里记录一下我所碰到的问题: meta 头部声明在开发的时候,刚刚创建 HTML 文件,再使用浏览器 ...
 - 回调函数,回调函数使用call
			
回调函数:一个函数b作为参数,给另外一个函数a使用.并且在执行a之后(注意不一定是执行完a),再去执行b这个函数. 上代码: function a(callback) { alert("我是 ...
 - Android 性能优化(7)网络优化( 3) Optimizing User-Initiated Network Use
			
Optimizing User-Initiated Network Use This lesson teaches you to Pre-fetch Network Data Check for Co ...
 - 在dataGridView空间中添加数据
			
//查询信息sql语句 string sql = "select studentName,addres from student"; SqlDataAdapter adapter ...