BFS(4):P1126 机器人搬重物———类dijkstra搜索,路径搜索去重

mac2024-04-18  36

输入输出样例 输入 #1复制 9 10 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 7 2 2 7 S 输出 #1复制 12

题意理解

机器人必须在最短的时间内把物品搬运到指定的地方 每个指令所需要的时间为1秒 请你计算一下机器人完成任务所需的最少时间 分别为起始点和目标点左上角网格的行与列

简单来说,我们需要从一个点移动到另外一个点,正常来说这是非常简单的事情; 这题的特点是移动的过程需要转向; 另外加就是机器人移动的过程具有体积; 另外就是障碍物也是有体积的。由于数据量并不太大,所以可能暂时不需要记忆化搜索。

搜索过程有一个关键是如何去除重复搜索的问题,就是这一题中如何标记才能防止反复搜索。

总结目录

1 如何解决本题的去除重复搜索问题 2 本题的建图过程,如何将格子图建立为点图 3 格子图变为点图后,搜索范围的确定过程,如何判定越界 4 本题复杂的逻辑,如何利用对象的思想进行编程,如何抽象状态

1 如何去除重复搜索

这题是要我们求一个最少的时间去到固定点的时间。对于这题的搜索,我们的状态至少需要包括位置row,col,以及当前的方向dir。那么除此之外我们还需不需要加上其他的状态?如果我从A->B后,我应该如何标记,才能把B->A的那个方案给剪枝掉呢?

在这一题中,使用了类似于dijkstra的最短路算法的方法。在dijkstra中,有一个距离数组dist[],只有当 当前结点搜索能够更新最短距离的情况下,才允许其入列,入列后更新该结点周围的dist[],利用这种方法,如果从A->B,再从B->A的话,距离一定是更长的,那么这种搜索的方案就会被排除掉!

类似的,在这题中,有一个距离矩阵timemark[][],这个矩阵用来记录能够到达当前结点的最短的时间,其实就类似于单源最短路径的算法。然后就是不断的搜索,只有当到达这个结点的时间是更优的情况下,才将这个结点看作是有意义的状态结点,并把它加入队列q。 那么为什么这个算法是正确的,为什么即使前面不好,后面也不会更好,这个原因类似于dijkstra的推理,这里就不再进行赘述了。

建图过程

本题中,需要将格子图重建为点图。这里对这种图扩散的总体思路做一个总结。格子图建为点图,需要先 把格子生成一定数量的点,然后再根据不同格子的位置把对应的点给染色为不同的数值。这里格子图的维度为N,M,那么对应点图就是n=N+1,m=M+1;染色的时候只需要染色 [row,col],[row+1,col+1],[row+1,col],[row,col+1] 即可。同理,如果是3X3的,也一样可以进行对应的映射染色。但是总体的流程都是 生成点阵,再去染不同的点。

搜索范围的确定

本题中有一个细节,就是机器人是有体积的,在格子图变为点图之后,机器人可以站的点的范围一定是row>=2,row<=n-1;col>=2,col<=m-1。 因此,这里不再是row<1,row>n了,也就是第1行和第n行都不再可行了。

复杂逻辑与面向对象

每个node的状态包含了row,col,time,dir。每次转向的时候,会更新对应的状态。因此我们写了turnright,turnleft等函数来对状态进行修改。

在pace()中需要判断整条路径上都可行,才是可行的。因为可以走多步,因此可能最终位置是可行的,但是路程上的方法是不可行的。

为了标定对应的方向,使用了map<char,int>来匹配。

一些debug的过程

对于a++的使用需要更谨慎一点。有时候想求+1的话,最好还是把数值+1,a++的话你可能不知道这个数值到底是返回什么。

换句话说,a++命令的返回你可能不确定是什么;但是a+1的运行值是很明显的一个值了。

另外就是int b[4]; b[i++];的访问就是先访问b[i],再i++;

Turnback函数的时间为+2;

越界的定义

重复搜索的去除

代码

#include<iostream> #include<map> #include<queue> #include<climits> #include<algorithm> #define maxsize 55 using namespace std; int inimat[maxsize][maxsize]; int mat[maxsize][maxsize]; int timemark[maxsize][maxsize]; int dir[][4] = { {0,1},{1,0},{0,-1},{-1,0} }; map<char, int> dir_vec = { {'E',0},{'S',1},{'W',2},{'N',3} }; char dir_hash[4] = { 'E','S','W','N' }; int N, M;//原始输入 int n, m;//点图对应的维度 void GridToPointMap() { //将格子图转换为点图 //先生成,生成对应数量的点 n = N + 1; m = M + 1; //染色,根据格子图将对应颜色的点进行染色 for (int row = 1; row <= n; row++) { for (int col = 1; col <= m; col++) { if (inimat[row][col] == 1) { mat[row][col] = 1; mat[row + 1][col + 1] = 1; mat[row][col + 1] = 1; mat[row + 1][col] = 1; } } } } struct node { //状态结点定义 int row; int col; char dir; int time; }; void TurnRight(node& obj) { obj.time++; char inidir = obj.dir; if (dir_vec[inidir] < 3) { obj.dir = dir_hash[dir_vec[inidir]+1];//这个+1写成++的话也会错 } else { obj.dir = dir_hash[0]; } } void TurnLeft(node& obj) { obj.time++; char inidir = obj.dir; if (dir_vec[inidir] > 0) { obj.dir = dir_hash[dir_vec[inidir]-1]; } else { obj.dir = dir_hash[3]; } } void TurnBack(node& obj) { obj.time=obj.time+2;//这里向后转时间花费是2秒 char inidir = obj.dir; switch (inidir) { case 'E': obj.dir = 'W'; break; case'W': obj.dir = 'E'; break; case'N': obj.dir = 'S'; break; case'S': obj.dir = 'N'; break; default: break; } } void turndir(node& obj, int dir_index) { //对obj对象进行方向调整,其中dir_index作为循环接口调用函数 switch (dir_index) { case 0: break; case 1: TurnRight(obj); break; case 2: TurnLeft(obj); break; case 3: TurnBack(obj); break; default: break; } } bool pace(node obj, int step) { //obj向当前方向行走step,在该过程中没有障碍 bool res = true; int currow = obj.row; int curcol = obj.col; int rowinc = dir[dir_vec[obj.dir]][0]; int colinc = dir[dir_vec[obj.dir]][1]; for (int i = 1; i <= step; i++) { int nextrow = currow + rowinc*i; int nextcol = curcol + colinc*i; if (nextrow>n-1||nextrow<2||nextcol>m-1||nextcol<2||mat[nextrow][nextcol] == 1) {//这个越界判别容易错 res = false;//越界+障碍判断,路径上只要有一步有阻碍,就跳出循环 break; } } return res; } node GoPoint(node obj, int step) { node res = obj; res.time++; res.row = res.row + dir[dir_vec[res.dir]][0] * step; res.col = res.col + dir[dir_vec[res.dir]][1] * step; return res; } int bfs(int srow, int scol, int trow, int tcol, char begindir) { if (srow == trow&&scol == tcol)return 0; node start; start.row = srow; start.col = scol; start.dir = begindir; start.time = 0; queue<node> q; q.push(start); while (!q.empty()) { node tmp = q.front(); q.pop(); //注意,这里不建议直接跳出直接返回结点的时间,因为当前结点虽然能够搜索到,但是并不一定是最小的 //如果要查找最小的时间,还是应该去搜索timemark[][]矩阵里面的结果! for (int i = 0; i < 4; i++) { node tmp2 = tmp; turndir(tmp2, i);//转向不同方向,转完方向后时间已经计算完转向时间 for (int step = 1; step <= 3; step++) { if (pace(tmp2, step)) {//如果该路径可以走 node tmp3=GoPoint(tmp2, step); if (tmp3.time < timemark[tmp3.row][tmp3.col]) {//如果这一步可以更新时间 timemark[tmp3.row][tmp3.col] = tmp3.time;//更新时间 q.push(tmp3);//入队 } } } } } if (timemark[trow][tcol] == INT_MAX) { return -1;//到达不了就是INT_MAX } else { return timemark[trow][tcol]; } } int main() { fill(timemark[0], timemark[0] + maxsize*maxsize, INT_MAX); cin >> N >> M; for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) { cin >> inimat[i][j];//原始地图输入 } } int s_row, s_col, t_row, t_col; char begin_dir; cin >> s_row >> s_col >> t_row >> t_col >> begin_dir; GridToPointMap();//格子图转换为点图 cout<<bfs(s_row+1, s_col+1, t_row+1, t_col+1, begin_dir);//起点是点,输入的是格子,因此要+1 return 0; }
最新回复(0)