POJ1475 推箱子---模块化思想

自古逢秋悲寂寥,我言秋日胜春朝。
晴空一鹤排云上,便引诗情到碧霄。 ——刘禹锡

题目:推箱子

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

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

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

求一种移动方案,使箱子移动的次数最少,在此基础上再让人移动的总步数最少。
方案中使用大写的“EWSN”(东西南北)表示箱子的移动,使用小写的“ewsn”(东西南北)表示人的移动。

POJ1475 推箱子---模块化思想_第1张图片

输入格式

输入包含多个测试用例。
对于每个测试用例,第一行包括两个整数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,就是该题的本质问题;

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

代码如下:

#include
#include
#include
#include
#include
#define pii pair 
//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  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  &s)
{
	queue  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  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  Q;
	stack  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 推箱子---模块化思想)