推箱子-求最少移动次数(分支限界法)

文章介绍了如何运用分支限界法解决一个推货物的迷宫类问题,其中涉及到目标函数、约束条件和限界函数的设定。程序代码使用C++编写,通过优先队列进行节点排序,并利用深度优先搜索判断机器人能否到达推货物的位置。最后,给出了实验的示例输入和输出,以及作者对于学习算法的热情和自我反思。
摘要由CSDN通过智能技术生成

 题目

ada5b1d897364fb8b594cbd3055cf1b9.png8b16c534cf184e27b928e5eaad50cbf2.png

求解过程分析

目标函数,约束条件,限界函数

设搜索的结点Node定义如下:

struct Node {

int box_x;

int box_y;

int robot_x;

int robot_y;

int step; // 已走步数

int dis; // 距离终点的距离

};

Node CurNode,next; // 当前堆顶结点,和下一待放入堆的结点

其中box_x,box_y为货物位置,robot_x,robot_y为机器人位置,step表示货物已经移动的步数,dis为货物距离终点end的最短移动距离。

map[][]为地图,vist[][]表示位置是否访问,GetPosition为判断机器人能否到达推货物位置的函数。

目标函数

CurNode.box_x=end_x && CurNode.box_y=end_y,表示到达终点

min CurNode.step,移动步数最少

约束条件:

map[next.box_x][next.box_y]=='.' && !visit[next.box_x][next.box_y]

&& GetPosition

表示该位置可到达并未被访问过,且机器人可移动到对应推货物的位置。

限界函数:

优先队列中按照Node.step + Node.dis作为每个结点所预测的下界来选择,即已移动距离加上当前位置到终点的最短的移动距离,越小的越优先。

解空间树

为迷宫类问题,空间树为一棵满四叉树,深度为m*n 

92990b45d24741498191d215066b096d.jpeg

堆的变化 

堆结点的值,包含货物坐标,机器人坐标,已移动距离step,距离目标的最短距离dis,小顶堆,按照step+dis大小。堆的变化过程如下图:

6162d1e7bab04cff8c8155f91cf5c30f.png

 搜索空间树

772205e9ee754ddea55d3fcf98af122f.png

 程序代码

/*分支限界法解决推货物问题*/
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include <queue>
using namespace std;
// 采用优先队列,所用步数最少且箱子位置离目标最近
struct Node {
	int box_x;													// 货物坐标
	int box_y;
	int robot_x;												//机器人坐标
	int robot_y;
	int step;													// 已走步数
	int dis;													// 距离终点的最短距离
	bool operator < (const Node& node) const
	{
		return step + dis > node.step + node.dis;				// 已走步数与剩余最短距离之和小的优先
	}
};
const int MAX = 20;
const int INF = 0x3f3f3f3f;
priority_queue<Node> q;											// 优先队列
char map[MAX][MAX];												// 地图
int visit[MAX][MAX];
int visit_dfs[MAX][MAX];
int n, m;
int NumOfNbrs = 4;
int offset[4][2] = { {-1, 0},{0, -1},{0, 1},{1, 0} };			// 位置偏移量,左上右下
// 利用dfs寻找推箱子的位置
void dfs(int to_x, int to_y, int box_x, int box_y, int robot_x, int robot_y, bool& flag)
{
	visit_dfs[robot_x][robot_y] = 1;
	if (to_x == robot_x && to_y == robot_y) {
		flag = true;
		return;
	}
	else {
		for (int i = 0; i < NumOfNbrs; i++) {
			int next_x = robot_x + offset[i][0];
			int next_y = robot_y + offset[i][1];
			if (next_x <= n && next_y >= 1 && next_y <= m && next_y >= 1 
				&& map[next_x][next_y] == '.' && !(next_x == box_x && next_y == box_y)
				&& visit_dfs[next_x][next_y] == 0) {
				visit_dfs[next_x][next_y] = 1;
				dfs(to_x, to_y, box_x, box_y, next_x, next_y, flag);
			}
		}
	}
}
// 查看机器人是否能够到达推货物的位置
bool GetPosition(int to_x, int to_y, int box_x, int box_y, int robot_x, int robot_y)
{
	if (map[to_x][to_y] != '.')
		return false;
	bool flag = false;
	dfs(to_x, to_y, box_x, box_y, robot_x, robot_y, flag);
	return flag;
}
// 推箱子
bool PushBox(int box_x, int box_y, int robot_x, int robot_y, int end_x, int end_y, int& best)
{

	if (box_x == end_x && box_y == end_y) {
		best = 0;
		return true;
	}
	// 初始化首个结点状态
	Node start;													
	Node here, next;
	start.box_x = box_x;
	start.box_y = box_y;
	start.robot_x = robot_x;
	start.robot_y = robot_y;
	start.step = 0;
	start.dis = abs(box_x - end_x) + abs(box_y - end_y);
	visit[start.box_x][start.box_y] = 1;
	q.push(start);
	while (!q.empty()) {
		here = q.top();
		q.pop(); 
		if (here.box_x == end_x && here.box_y == end_y) {
			best = here.step;
			return true;
		}
		for (int i = 0; i < NumOfNbrs; i++) {
			// 初始化下一个目标位置,满足条件则入队
			next.box_x = here.box_x + offset[i][0];
			next.box_y = here.box_y + offset[i][1];
			next.robot_x = here.box_x;
			next.robot_y = here.box_y;
			next.step = here.step + 1;
			next.dis = abs(next.box_x - end_x) + abs(next.box_y - end_y);
			int to_x = here.box_x - offset[i][0];
			int to_y = here.box_y - offset[i][1];
			memset(visit_dfs,0,sizeof(visit_dfs)); // 初始化为0,用于确定推货物的位置是否可达
			if (map[next.box_x][next.box_y] == '.' && !visit[next.box_x][next.box_y] 
				&& GetPosition(to_x, to_y, here.box_x, here.box_y, here.robot_x, here.robot_y)) {
				visit[next.box_x][next.box_y] = 1;
				q.push(next);
			}
		}
	}
	return false;
}
int main()
{
	// 初始化
	int best = INF;
	int box_x = 0, box_y = 0;
	int robot_x = 0, robot_y = 0;
	int end_x = 0, end_y = 0;
	memset(visit,0,sizeof(visit));
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			cin >> map[i][j];
			if (map[i][j] == 'B') {
				box_x = i;
				box_y = j;
				map[i][j] = '.';
			}
			if (map[i][j] == 'T') {
				end_x = i;
				end_y = j;
				map[i][j] = '.';
			}
			if (map[i][j] == 'S') {
				robot_x = i;
				robot_y = j;
				map[i][j] = '.';
			}
		}
	//for (int i = 1; i <= n; i++) {
	//	for (int j = 1; j <= m; j++) {
	//		cout << map[i][j];
	//	}
	//	cout << endl;
	//}

	if (PushBox(box_x, box_y, robot_x, robot_y, end_x, end_y, best))
		cout << best;
	else cout << "no solution";
	//cout << GetPosition(4, 5, 4, 6, 6, 7);
	return 0;
}
/*
7 11
###########
#T##......#
#.#.#..####
#....B....#
#.######..#
#.....S...#
###########
7 11
###########
#.##....T.#
#.#.#..####
#....B....#
#.######..#
#.....S...#
###########
7 11
###########
#.#.S...T.#
#.#.#..####
#....B....#
#.###.##..#
#.........#
###########
*/

小结

这是我本学期实验课最后两个实验之一,虽然完成了这个实验,但是还是参考了相关代码,并花了不少时间修改调试,毕竟自己能力非常不足。发这个博客我也只是想记录下自己的学习历程,提醒一下自己不能再颓废下去了,毕竟马上要期末了。最后也希望我能继续有着对学习算法的热情吧,即使相对而言自己很愚笨,专业知识能力很欠缺。

本人能力有限,实验求解和分析过程如有错误也请批评指正。

´问题描述: 码头仓库是划分为n×m个格子的矩形阵列。有公共边的格子是相邻格子。当前仓库中 有的格子是空闲的;有的格子则已经堆放了沉重的货物。由于堆放的货物很重,单凭仓库管 理员的力量是无法移动的。仓库管理员有一项任务,要将一个小箱子推到指定的格子上去。 管理员可以在仓库中移动,但不能跨过已经堆放了货物的格子。管理员站在与箱子相对的空 闲格子上时,可以做一次推动,把箱子推到另一相邻的空闲格子。推箱时只能向管理员的对 面方向推。由于要推动的箱子很重,仓库管理员想尽量减少推箱子次数。 ´编程任务: 对于给定的仓库布局,以及仓库管理员在仓库中的位置和箱子的开始位置和目标位置, 设计一个解推箱子问题的分支限界法, 计算出仓库管理员将箱子从开始位置推到目标位置所 需的最少推动次数。 ´数据输入: 由文件input.txt提供输入数据。输入文件第 1 行有 2个正整数 n和 m(1<=n,m<=100) , 表示仓库是n×m个格子的矩形阵列。接下来有 n行,每行有 m个字符,表示格子的状态。 S 表示格子上放了不可移动的沉重货物; w 表示格子空闲; M 表示仓库管理员的初始位置; P 表示箱子的初始位置; K 表示箱子的目标位置。 ´结果输出: 将计算出的最少推动次数输出到文件 output.txt。如果仓库管理员无法将箱子从开始位 置推到目标位置则输出“No solution!” 。 输入文件示例 输出文件示例 input.txt output.txt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值