POJ1475 推箱子---模块化思想
自古逢秋悲寂寥,我言秋日胜春朝。
晴空一鹤排云上,便引诗情到碧霄。 ——刘禹锡
题目:推箱子
网址: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 推箱子---模块化思想的更多相关文章
- 推箱子 Sokoban(华中农业比赛)
点这里 打开题目链接 点击打开链接 题目就是我们玩过的推箱子: 一顿暴力广搜:加状态标记.状态压缩需要用到一个类似于康拓的思想来压缩:所以容易TLE,搜索就是用一个int型的数字来表示一个状态, ...
- hdu 1254 推箱子(嵌套搜索,bfs中有dfs)
推箱子 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...
- OC推箱子
#include<stdio.h> #include<stdlib.h> int main(void) { char sr;//存储用户输入的指令 //绘制地图 char a[ ...
- c语言游戏推箱子
前两天做了推箱子小游戏,看似简单的一个小游戏背后却 有巨大的秘密,这秘密就是一大堆逻辑. 自从学习了函数过后,的确是解决了很多问题,而且调用很方便,尽管我现在都不是很会调用. 写完一个函数,准备测试一 ...
- JavaScript写一个小乌龟推箱子游戏
推箱子游戏是老游戏了, 网上有各种各样的版本, 说下推箱子游戏的简单实现,以及我找到的一些参考视频和实例: 推箱子游戏的在线DEMO : 打开 如下是效果图: 这个拖箱子游戏做了移动端的适配, 我使用 ...
- 用C#制作推箱子小游戏
思路分析: 一.制作一个地图 二.地图中放置墙.箱子.人.目标等 三.让小人动起来完成推箱子动作 游戏制作: 1.按照上述地图制作一个地图 (12行×13列) 地图可以看做是行和列组成的,即可以看做 ...
- hdu.1254.推箱子(bfs + 优先队列)
推箱子 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submis ...
- [游戏模版17] Win32 推箱子 迷宫
>_<:Here introduce a simple game: >_<:resource >_<:only can push a box and finally ...
- [转]Flash ActionScript2.0面向对象游戏开发-推箱子
本文转自:http://www.alixixi.com/Dev/W3C/Flash/2007/2007070868666.html 概述: Flash ActionScript2.0是一种面向对向的编 ...
随机推荐
- Python第三方包之离线安装
Python第三方包之离线安装 第一步 首先我们得从pypi上先下载要装的第三方包 https://pypi.org/ 第二步(因为下载下来的包可能需要其他包的依赖,那我们依旧要按照第一步再次下载) ...
- Ruby学习计划-(0)前言
前言 接触过的编程语言现在来说有C.C++.Java.Object-C.Swift.这几门语言中最熟悉的就是oc了,毕竟靠他吃饭,对于其他几门语言都是初入门径而已,由于本身一直从事iPhone ...
- # CodeCraft-20 (Div. 2)
CodeCraft-20 (Div. 2) A. Grade Allocation 思路 : 无脑水题 代码 #include<iostream> #include<algorith ...
- fiddler设置重定向
fiddler支持将指定模式的url映射到另一个指定的url,即可用于拦截某一请求,并重定向到本地的资源或其他服务器地址 fiddler常用于: 解析请求:如:解析http请求状态,请求头,请求正文, ...
- PTA数据结构与算法题目集(中文) 7-31
PTA数据结构与算法题目集(中文) 7-31 7-31 笛卡尔树 (25 分) 笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2.首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有 ...
- 【WPF学习】第六十六章 支持可视化状态
上一章介绍的ColorPicker控件,是控件设计的最好示例.因为其行为和可视化外观是精心分离的,所以其他设计人员可开发动态改变其外观的新模板. ColorPicker控件如此简单的一个原因是不涉及状 ...
- Python爬虫系列(二):requests基础
1.发送请求: import requests # 获取数据#r是一个 response 对象.包含请求返回的内容r = requests.get('https://github.com/timeli ...
- 《SQL 反模式》 学习笔记
第一章 引言 GoF 所著的的<设计模式>,在软件领域引入了"设计模式"(design pattern)的概念. 而后,Andrew Koenig 在 1995 年造了 ...
- Hadoop学习笔记(2)-HDFS的基本操作(Shell命令)
在这里我给大家继续分享一些关于HDFS分布式文件的经验哈,其中包括一些hdfs的基本的shell命令的操作,再加上hdfs java程序设计.在前面我已经写了关于如何去搭建hadoop这样一个大数据平 ...
- Jmeter命令行执行并生成HTML报告
前提:准备好jmeter脚本,找到jmeter配置文件查看生成的日志格式是否为csv,如果不是请改为csv 注意:使用命令执行jmeter脚本必须使用jmeter 3.0及以上版本1.使用命令行执行脚 ...