简介:
IEEE国际标准电脑鼠走迷宫竞赛,简单来说是使用微控制器、传感器和机电运动部件构成的一种智能行走机器人,本文仅对”到达迷宫终点",这一问题进行算法上的思索与解决。
方法:
1-DFS(深度优先搜索):
- 算法简介:dfs,又叫深搜,暴搜,通过朴实的暴力来解决问题。同一道题目,搜索的方式有很多,最重要的是找到一种顺序去进行搜索。一般通过二叉树进行递归形式的回溯搜索,例如我们有寻找1-3的全排列,如下图(图片来自Hasity,感谢大佬)
一共三个坑,一个坑填一个数,我们通过开设一个状态数组来标记当前尚未使用的数,每次填入坑中,当一个节点无路可走或走到头是,回溯到最近的一个节点去搜索下一条路
核心操作:
void dfs(int u)
{
if(u == n)//坑满了
{
for(int i = 0; i < n; i ++)
cout << path[i] << " ";
cout << endl;
return;
}
for(int i = 1; i <= n; i ++)
if(!st[i])//找没用过的填坑
{
path[u] = i;//填坑
st[i] = true;
dfs(u + 1);//分支向下走
st[i] = false;//恢复现场
}
}
- 小迷宫,走起来
思路:小鼠从起点出发,一旦撞墙就返回到上一岔路口,尝试不同的转向,尝试过的道路标记一下,不走回头路浪费时间,不断深入迷宫,最终到达终点。
题境:给出一个n*m的01迷宫,0可走,1不可走,从(1, 1)走到(n, m)
核心操作:
void dfs(int x, int y, int cnt)
{
if(x == n && y == m)
{
tt ++;//记录从起点到终点的方案数
if(cnt < ans)//记录最短步数
ans = cnt;
return;
}
int f[5] = {1, 0, -1, 0, 1};//方向数组,四方通行
for(int i = 0; i < 4; i ++)
{
int xx = x + f[i], yy = y + f[i + 1];
if(xx > 0 && yy > 0 && xx <= n && yy <= m && !g[xx][yy] && !st[xx][yy])
{
st[xx][yy] = 1;
dfs(xx, yy, cnt + 1);
st[xx][yy] = 0;//及时回溯,下一条路
}
}
}
- 分析:深度搜索在走迷宫时只有走到死路才会返回,遍历所有可能的路线直到终点才能找到最短路径,适合枚举所有路线方案,但对于速度为王的迷宫来说,并非上策。当然也可以通过添加一些条件,少走一些弯路,达到’剪枝优化’的目的。
2-BFS(广度优先搜索):
- 算法简介:bfs,又叫宽搜,通过广度优先遍历,一层层的向外拓展,搜索所有距离为i++的点,那么第一次搜到的目标距离即为最短距离。
- 如图,题意与dfs的小迷宫一样,从(1,1)走到(5,5),我们每次都只看比当前位置+1的点,一层层的向外拓展,直到终点出现,即为最短距离。(下图来自Acwing-y总,最生动形象,感谢y总)
- 小迷宫,走起来(2.0)
思路:小鼠从起点出发,沿着交叉路口的某一分支前进,到下一个路口的时候回去检查,反复循环,达到一层层向外拓展的目的,直到找到终点。(听起来好像更麻烦了,但在实操里时间复杂度还是比暴搜低的)
代码思路:用一个队列来实现(STL大法,数组模拟自己够呛),开一个距离数组,来表示每个点到起点的距离,初始为-1,存入起点,开始上下左右向外拓展,每到一个新位置,距离+1,并存入新位置,注意,当开始探索下一位置时,岔路口的先前存入的坐标要弹出队列,表示不再回头记录。最终当队列为空时,即无路可走,到达终点,此时终点的距离数组的坐标即为所求最短距离。(描述粗糙,见谅)
核心操作:
int bfs()
{
queue<PII> q;
q.push({0, 0});
memset(d, -1, sizeof d);//距离数组初始化
d[0][0] = 0;
while(!q.empty())
{
auto t = q.front();
q.pop();//勿忘弹出,再也不见
for(int i = 0; i < 4; i ++)//方向数组同上
{
int x = t.first + f[i], y = t.second + f[i + 1];
if(x < n && y < m && x >= 0 && y >= 0 && g[x][y] == 0 && d[x][y] == -1)
{
d[x][y] = d[t.first][t.second] + 1;//比上一步距离起点+1
q.push({x, y});//这点要压入队列
}
}
}
return d[n - 1][m - 1];//最终答案,终点距离起点的最短距离
}
- 分析:广度优先搜索从结果上来看,层层拓展的找寻方式的相对暴搜少走许多弯路,但还是需要小鼠不断去尝试进行移动。
走向实际:
洪水填充算法:一张图上有多个区域,不同的区域用不同颜色区分,同一个区域的所有点的颜色 (oldColor) 都是相同的。给定图上的一个点,称为种子点,然后从种子点出发,把种子点所属的封闭区域用新颜色 (fillColor) 填充,这就是 “洪水填充”。(了解不深)
实现:用DFS和BFS均可进行实现,上图为通用的四邻域填充法,当然也有八向填充,接受三个参数—起始节点,目标节点以及提取对象要执行的处理。
实际:假设小鼠与终点之间无障碍,小鼠可以直接到达终点,此时在这条"最短路径"上,遇到墙壁便标记起来,并重新规划路线,一直朝终点前进。"祖传代码"会标记出每一点距离终点的距离,碰到墙壁处返回,数字重新标记,此时小鼠沿着递减路线直达终点便是最短路线。任何事不会有完美的,洪水填充算法,不保证第一次便找到最佳路径,但其进一步优化了策略,避免了行走许多重复路线。
杂谈:
算法总归要应用于实际,电脑鼠走迷宫的比赛需要电子信息,程序设计,机械工程,自动控制,传感与测试等多项技术知识。向小鼠走迷宫,我们往往会将目光局限在"最短",可最短不一定最快,转弯速度同样影响平均速度,比起现实,我们需要考虑的还远远不止编译器上的几行代码。
在近期的全国大学生机器人大赛高校对抗赛(机甲大师赛)中,电子科技大学的兔子机器人一跃而起,抢占先机,赢下半决赛冠军,并在之后与广州城市理工学院的决赛中,跳跃式拿下最后总冠军。这是一场创新性的比赛,不拘泥于传统,推陈出新,科技的进步同样如此。
推荐到B站去观看,从预选赛到最后的决赛均有,观感不错。
本文参考:(https://zhuanlan.zhihu.com/p/51538950),b站洪水填充,acwing