题目
求解过程分析
目标函数,约束条件,限界函数
设搜索的结点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
堆的变化
堆结点的值,包含货物坐标,机器人坐标,已移动距离step,距离目标的最短距离dis,小顶堆,按照step+dis大小。堆的变化过程如下图:
搜索空间树
程序代码
/*分支限界法解决推货物问题*/
#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....#
#.###.##..#
#.........#
###########
*/
小结
这是我本学期实验课最后两个实验之一,虽然完成了这个实验,但是还是参考了相关代码,并花了不少时间修改调试,毕竟自己能力非常不足。发这个博客我也只是想记录下自己的学习历程,提醒一下自己不能再颓废下去了,毕竟马上要期末了。最后也希望我能继续有着对学习算法的热情吧,即使相对而言自己很愚笨,专业知识能力很欠缺。
本人能力有限,实验求解和分析过程如有错误也请批评指正。