自古逢秋悲寂寥,我言秋日胜春朝。

晴空一鹤排云上,便引诗情到碧霄。 ——刘禹锡

题目:推箱子

网址:http://poj.org/problem?id=1475

推箱子游戏相信大家都不陌生,在本题中,你将控制一个人把1个箱子到目的地。

给定一张N行M列的地图,用字符”.”表示空地,字符”#”表示墙,字符”S”表示人的起始位置,字符”B”表示箱子的起始位置,字符”T”表示箱子的目标位置。

求一种移动方案,使箱子移动的次数最少,在此基础上再让人移动的总步数最少。

方案中使用大写的“EWSN”(东西南北)表示箱子的移动,使用小写的“ewsn”(东西南北)表示人的移动。

输入格式

输入包含多个测试用例。

对于每个测试用例,第一行包括两个整数N,M。

接下来N行,每行包括M个字符,用以描绘整个N行M列的地图。

当样例为N=0,M=0时,表示输入终止,且该样例无需处理。

输出格式

对于每个测试用例,第一行输出”Maze #”+测试用例的序号。

第二行输入一个字符串,表示推箱子的总体移动过程,若无解,则输出”Impossible.”。

每个测试用例输出结束后输出一个空行。

若有多条路线满足题目要求,则按照N、S、W、E的顺序优先选择箱子的移动方向(即先上下推,再左右推)。

在此前提下,再按照n、s、w、e的顺序优先选择人的移动方向(即先上下动,再左右动)。

数据范围

1≤N,M≤20

输入样例:
1 7
SB....T
1 7
SB..#.T
7 11
###########
#T##......#
#.#.#..####
#....B....#
#.######..#
#.....S...#
###########
8 4
....
.##.
.#..
.#..
.#.B
.##S
....
###T
0 0
输出样例:
Maze #1
EEEEE Maze #2
Impossible. Maze #3
eennwwWWWWeeeeeesswwwwwwwnNN Maze #4
swwwnnnnnneeesssSSS

这道题有点难,我们不妨可以这样考虑进行假设:

  • 若没有箱子,我们可以使用广搜求出人到任意一点的最短距离;

题意明确要求先保证箱子步数最小,再保证人走的路程最短,并要给出具体方案;

先考虑如何保证箱子步数最小,或者说如何求解这个最小值呢?

考虑状态(box_x, box_y, man_x, man_y),对于该状态进行广度优先搜索即可。

我们考虑,请试想:该状态扩展时,会扩展出什么呢?箱子显然不可能向任意方向扩展,只有当人在箱子的一侧,将其“向前”推到符合题意的位置(无障碍),进行一次扩展操作;人的扩展可以枚举四个方向,每次扩展到没有障碍的位置时,将新状态放于队尾进行扩展。

这样,当箱子处于目标位置即为最小值了。

你做错了! !

还记得广度优先搜索满足什么性质吗?单调性。

那么,队列中什么满足单调性呢?状态满足单调性。那么,我们刚刚的做法,实际上是以总步数作为关键字,总步数单调!

而实际上题目要求箱子移动步数最优的前提下再保证人步数最小值。换言之,箱子移动步数是第一关键字,而在此满足最优的情况下,人作为第二关键表示在状态中了。

那么,对于每个状态,我们只能定义关于箱子的位置,而并不能带上人的坐标。

对于人走了多少步,我们可以在对其中进行BFS扩展;

总结:对于推箱子这个问题,其实是箱子走迷宫问题,状态的每一次扩展时所需要的人最少步数,恰又是一个BFS。

大的BFS套着一个BFS,这便是所谓的“双重BFS”!

其实,不管题目有多么复杂,如果每次考虑问题都先整体去考虑,对于细节的求解假定它是可求的,那么,当大框架大部署确定后,问题便一目了然,无论是算法设计还是代码实现。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<queue>
#define pii pair <int , int>
//To simplify the questions, we prefer to use pair and make first -> x, second -> y.
#define x first
#define y second
using namespace std;
struct rec
{
int x, y, dir;
} man, box, tg;
//tg -> target
const int MAX_size = 20 + 10;
const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
const char cdir[5] = {'N', 'S', 'W', 'E', '\0'}, ddir[5] = {'n', 's', 'w', 'e', '\0'}; //up 0 down 1 left 2 right 3
queue <char> ans;
bool book[MAX_size][MAX_size][4];
bool mat[MAX_size][MAX_size];
char map[MAX_size][MAX_size];
int n, m;
int d[MAX_size][MAX_size], d_man[MAX_size][MAX_size][4], f_box[MAX_size][MAX_size][4], d_box[MAX_size][MAX_size][4];
int f_man[MAX_size][MAX_size];
void init()
{
memset(f_man, -1, sizeof(f_man));
memset(f_box, -1, sizeof(f_box));
memset(d_man, -1, sizeof(d_man));
memset(d_box, -1, sizeof(d_box));
memset(mat, false, sizeof(mat));
memset(book, false, sizeof(book));
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
if(map[i][j] == 'S')man.x = i, man.y = j;
else if(map[i][j] == 'B')box.x = i, box.y = j;
else if(map[i][j] == 'T')tg.x = i, tg.y = j, tg.dir = -1;
}
}
return;
} bool valid(int x, int y)
{
if(x < 1 || x > n || y < 1 || y > m || map[x][y] == '#') return 0;
return 1;
} int expand(pii st, pii ed, stack <char> &s)
{
queue <pii> Q;
while(!s.empty()) s.pop();
while(!Q.empty()) Q.pop();
if(st.x == ed.x && st.y == ed.y) return 0;
Q.push(st);
mat[st.x][st.y] = true;
d[st.x][st.y] = 0;
while(!Q.empty())
{
pii now = Q.front(), next;
Q.pop();
for(int k = 0; k < 4; ++ k)
{
next = make_pair(now.x + dx[k], now.y + dy[k]); if(!valid(next.x, next.y) || mat[next.x][next.y]) continue; f_man[next.x][next.y] = k;
mat[next.x][next.y] = true;
d[next.x][next.y] = d[now.x][now.y] + 1;
if(next.x == ed.x && next.y == ed.y)
{
int prev_x = next.x, prev_y = next.y, tmp_1, tmp_2;
while(!(prev_x == st.x && prev_y == st.y))
{
s.push(ddir[f_man[prev_x][prev_y]]);
tmp_1 = prev_x - dx[f_man[prev_x][prev_y]];
tmp_2 = prev_y - dy[f_man[prev_x][prev_y]];
prev_x = tmp_1, prev_y = tmp_2;
}
return d[next.x][next.y];
}
Q.push(next);
}
}
return -1;
} void _print(int x, int y, int dir)
{
stack <char> s;
if(x == box.x && y == box.y && f_box[x][y][dir] == -1)
{
mat[box.x][box.y] = true;
expand(make_pair(man.x, man.y), make_pair(box.x - dx[dir], box.y - dy[dir]), s);
memset(mat, false, sizeof(mat));
while(!s.empty())
{
ans.push(s.top());
s.pop();
}
return;
}
int prev_dir = f_box[x][y][dir], prev_x = x - (dx[dir] + dx[prev_dir]), prev_y = y - dy[dir] - dy[prev_dir];
_print(x - dx[dir], y - dy[dir], prev_dir);
mat[x - dx[dir]][y - dy[dir]] = true;
expand(make_pair(prev_x, prev_y), make_pair(x - (dx[dir] * 2), y - (dy[dir] * 2)), s);
memset(mat, false, sizeof(mat));
while(!s.empty())
{
ans.push(s.top());
s.pop();
}
ans.push(cdir[dir]);
return;
} bool bfs()
{
queue <rec> Q;
stack <char> s;
while(!Q.empty()) Q.pop();
int k;
for(k = 0; k < 4; ++ k)
{
int &num = d_box[box.x][box.y][k], &cnt = d_man[box.x][box.y][k];
if(!valid(box.x - dx[k], box.y - dy[k])) continue;
mat[box.x][box.y] = true;
cnt = expand(make_pair(man.x, man.y), make_pair(box.x - dx[k], box.y - dy[k]), s);
memset(mat, false, sizeof(mat));
if(cnt == -1) continue;
Q.push(rec {box.x, box.y, k});
book[box.x][box.y][k] = true;
num = 0;
}
while(!Q.empty())
{
rec now = Q.front(), next;
Q.pop();
for(k = 0; k < 4; ++ k)
{
next.x = now.x + dx[k];
next.y = now.y + dy[k];
next.dir = k;
if(!valid(next.x, next.y)) continue;
int &num = d_box[next.x][next.y][k], &cnt = d_man[next.x][next.y][k] , tmp;
mat[now.x][now.y] = true;
tmp = expand(make_pair(now.x - dx[now.dir], now.y - dy[now.dir]), make_pair(now.x - dx[k], now.y - dy[k]), s);
memset(mat, false, sizeof(mat));
if(tmp == -1) continue;
if(book[next.x][next.y][next.dir])
{
if(num >= d_box[now.x][now.y][now.dir] + 1 && cnt > tmp + d_man[now.x][now.y][now.dir])
{
num = d_box[now.x][now.y][now.dir] + 1;
cnt = d_man[now.x][now.y][now.dir] + tmp;
f_box[next.x][next.y][k] = now.dir;
}
continue;
}
book[next.x][next.y][k] = true;
f_box[next.x][next.y][next.dir] = now.dir;
num = d_box[now.x][now.y][now.dir] + 1;
cnt = d_man[now.x][now.y][now.dir] + tmp;
if(next.x == tg.x && next.y == tg.y)
{
if(tg.dir == -1)
tg.dir = next.dir;
else
{
if(num > d_box[tg.x][tg.y][tg.dir])
{
_print(tg.x, tg.y, tg.dir);
return true;
}
else if(cnt < d_man[tg.x][tg.y][tg.dir]) tg.dir = next.dir;
}
}
Q.push(next);
}
}
if(tg.dir != -1)
{
_print(tg.x, tg.y, tg.dir);
return true;
}
return false;
} int main()
{
int T = 1;
while(scanf("%d %d", &n, &m) == 2 && n && m)
{
printf("Maze #%d\n", (T ++));
for(int i = 1; i <= n; ++ i)
{
scanf("%s", map[i] + 1);
}
init();
if(bfs())
{
while(!ans.empty())
{
printf("%c", ans.front());
ans.pop();
}
puts("");
}
else puts("Impossible.");
puts("");
}
return 0;
}

POJ1475 推箱子---模块化思想的更多相关文章

  1. 推箱子 Sokoban(华中农业比赛)

    点这里 打开题目链接   点击打开链接 题目就是我们玩过的推箱子: 一顿暴力广搜:加状态标记.状态压缩需要用到一个类似于康拓的思想来压缩:所以容易TLE,搜索就是用一个int型的数字来表示一个状态, ...

  2. hdu 1254 推箱子(嵌套搜索,bfs中有dfs)

    推箱子 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  3. OC推箱子

    #include<stdio.h> #include<stdlib.h> int main(void) { char sr;//存储用户输入的指令 //绘制地图 char a[ ...

  4. c语言游戏推箱子

    前两天做了推箱子小游戏,看似简单的一个小游戏背后却 有巨大的秘密,这秘密就是一大堆逻辑. 自从学习了函数过后,的确是解决了很多问题,而且调用很方便,尽管我现在都不是很会调用. 写完一个函数,准备测试一 ...

  5. JavaScript写一个小乌龟推箱子游戏

    推箱子游戏是老游戏了, 网上有各种各样的版本, 说下推箱子游戏的简单实现,以及我找到的一些参考视频和实例: 推箱子游戏的在线DEMO : 打开 如下是效果图: 这个拖箱子游戏做了移动端的适配, 我使用 ...

  6. 用C#制作推箱子小游戏

    思路分析: 一.制作一个地图 二.地图中放置墙.箱子.人.目标等 三.让小人动起来完成推箱子动作 游戏制作: 1.按照上述地图制作一个地图  (12行×13列) 地图可以看做是行和列组成的,即可以看做 ...

  7. hdu.1254.推箱子(bfs + 优先队列)

    推箱子 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submis ...

  8. [游戏模版17] Win32 推箱子 迷宫

    >_<:Here introduce a simple game: >_<:resource >_<:only can push a box and finally ...

  9. [转]Flash ActionScript2.0面向对象游戏开发-推箱子

    本文转自:http://www.alixixi.com/Dev/W3C/Flash/2007/2007070868666.html 概述: Flash ActionScript2.0是一种面向对向的编 ...

随机推荐

  1. 并查集例题02.带权并查集(poj1182)

    Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A.现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底 ...

  2. Python学习笔记:函数详解(下)

    本文介绍:高阶函数,嵌套函数,以及由前面两个组成的装饰器 一.高阶函数:以下两种情况都是高阶函数   1.将函数名当成参数传递给另外一个函数(作用:不修改被传递函数源代码就可以添加新功能): impo ...

  3. 1062 Talent and Virtue (25分)(水)

    About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about ...

  4. php 直接跳出嵌套循环

    break 结束当前 for,foreach,while,do-while 或者 switch 结构的执行. break 可以接受一个可选的数字参数来决定跳出几重循环. <?php $arr = ...

  5. 一个lock锁就可以分出低中高水平的程序员对问题的处置方式

    说到lock锁,我相信在座的各位没有不会用的,而且还知道怎么用不会出错,但让他们聊一聊为什么可以锁住,都说人以群分,大概就有了下面低中高水平的三类人吧. 第一类人 将lock对象定义成static,这 ...

  6. leetcode c++做题思路和题解(1)——常规题总结

    常规题总结 0. 目录 两数之和 1. 两数之和 耗时4ms(98.82%),内存6.2m. 两数之和--寻找中值向两边扩散法 1.1 思路 思路很简单,就是先找数组中target/2的前后两个值,然 ...

  7. tf.nn.sigmoid_cross_entropy_with_logits 分类

    tf.nn.sigmoid_cross_entropy_with_logits(_sentinel=None,,labels=None,logits=None,name=None) logits和la ...

  8. python 自动生成model 文件 案例分析

    生成方式 Python中想要自动生成 model文件可以通过 sqlacodegen这个命令来生成对应的model文件 sqlacodegen 你可以通过pip去安装: pip install sql ...

  9. java web之Filter详解

    java web之Filter详解 2012-10-20 0 个评论 作者:chenshufei2 收藏 我要投稿 .概念: Filter也称之为过滤器,它是Servlet技术中比较激动人心的技术,W ...

  10. 掉了10根头发都无法解决的数学题,python帮你完美解答

    本来这个周末过得开开心心,结果为了解一道数学题薅掉了一把头发...整整10根! 而且还是一道小学数学题!!! 到底是什么题呢?大家看看吧 这不就是一道逻辑题嘛! 先假如丁错,则甲乙丙对,此时最小的ab ...