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


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

    题目:推箱子

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

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

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

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

    image

    输入格式

    输入包含多个测试用例。
    对于每个测试用例,第一行包括两个整数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', ''}, ddir[5] = {'n', 's', 'w', 'e', ''}; //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
    ", (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;
    }
    
  • 相关阅读:
    通配符^与not like 区别
    SQL语句
    身份证的性别验证(摘抄)
    基于VirtualBox虚拟机安装Ubuntu教程
    VMware手动添加centos7硬盘图文操作及分区超详细
    acl权限命令
    linux查看分区是否开启acl权限
    CentOS7上Docker简单安装及nginx部署
    Docker安装ngnix使用ping报错
    centos7安装mysql5.6
  • 原文地址:https://www.cnblogs.com/zach20040914/p/12633139.html
Copyright © 2020-2023  润新知