思路:在解决之前的函数时,我们判断函数是否可以落脚,标记可落脚的函数都是通用的,而现在由于迷宫的通路是带环的,如果我们在使用上面提到的不带环的多通路求最短路径的方法,肯定这种结果是错误的,所以对于带环的迷宫求解我们再也不能像以前简单的标记为2,我们应该标记的是走过的步数,这样我们判断是否能落脚时,可以直接判断下一步的步数是否比我们即将标记的步数大,如果大的话,就继续进行落脚标记,否则就不可以。
1) 判断当前点是否能落脚,不能落脚就直接返回
2)能落脚就标记当前点,并且将当前点插入到cur_path中
3) 判定当前点是否为出口,是出口就说明找到了一条路
4) 将这条路与最短路径进行比较,把比较短的路径保存到最短路径当中
5) 不管当前路径是否为最短路径都要进行回溯,回溯到前一个点继续进行查找其他的路径
6)如果不是出口,就探测相邻的4个点,(采用顺时针的方法进行回溯)每探测一个点就调用函数本身,重复1~6的动作。
7)如果我们的四个方向都进行探测过了,就可以进行出栈了(cur_path,short_path的栈顶元素进行出栈)回溯到上一个点
代码的实现:
/////////////////////////////////////////////////////////
/////////////////带环多通路最短路径//////////////////////
/////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
void MazeInitCycle(Maze *maze)
{
int map[ROW][COL] =
{
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 0 },
{ 1, 1, 0, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1 },
{ 0, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0 }
};
int i = 0;
for (; i < ROW; i++)
{
int j = 0;
for (; j < COL; j++)
{
maze->map[i][j] = map[i][j];
}
}
}
//判断是否能落脚的函数
int CanStayWithCycle(Maze *maze, Point cur, Point pre)
{
//在地图外的点都不能落脚
if (cur.row < 0 || cur.row >= ROW || cur.col < 0 || cur.col >= COL)
{
return 0;
}
//在地图内的点,且值为1就能直接落脚
int cur_value = maze->map[cur.row][cur.col];
int pre_value = maze->map[pre.row][pre.col];
if (cur_value == 1)
{
return 1;
}
//当前点如果已经走过了,比较cur对应的值和pre对应的值
//cur-value = 7,pre-value = 5应该落脚
//cur-value = 6,pre-value = 5不应该落脚
//cur-value = 5,pre-value = 5不应该落脚
//cur-value = 4,pre-value = 5不应该落脚
//cur-value > pre-value+1就应该落脚
if (cur_value > pre_value + 1)
{
return 1;
}
return 0;
}
//标记函数
void MarkWithCycle(Maze *maze, Point cur, Point pre)
{
if (pre.row == -1 && pre.col == -1)
{
//针对入口点进行标记,此时的pre是一个非法点
//不能根据pre_value+1的方法进行标记
//由于地图中我们用1表示路,所以这里对于入口点直接标记为2,所以对于后面的标记的数字的意义就是对该数字-1就是从入口点走到该点的所需的步数
maze->map[cur.row][cur.col] = 2;
return;
}
int pre_value = maze->map[pre.row][pre.col];
maze->map[cur.row][cur.col] = pre_value + 1;
}
//每次走到下一个点都会递归的调用这个函数
void GetCycleShort(Maze *maze, Point cur, Point pre, Point entry, SeqStack *cur_path, SeqStack *short_path)
{
if (maze == NULL || cur_path == NULL || short_path == NULL)
{
return;
}
//判断当前点是否能落脚(判定规则改变了)
if (!CanStayWithCycle(maze, cur, pre))
{
//不能落脚就直接返回
return;
}
//能落脚就把当前点标记(标记规则也改变了)并且入cur_path栈,cur_path栈保存着我们走过的路径
MarkWithCycle(maze, cur, pre);
SeqStackPush(cur_path, cur);
//更新pre的值,后续判断是否可以落脚标记
pre = cur;
//判定当前点是否为出口
if (IsExit(maze, cur, entry))
{
//是出口说明找到了一条路
printf("找到了一条路\n");
//将cur_path与short_path进行比较
if (short_path->size == 0 || cur_path->size < short_path->size)
{
printf("找到了一条比较短的路\n");
//把比较短的路径保存到short_path栈中
SeqStackAssgin(cur_path, short_path);
}
//不管当前的路径是否为最短的路径都要进行回溯,回到前一个点继续找其他的路径
SeqStackPop(cur_path);
return;
}
//如果不是出口,以当前点为基准点,探测相邻的四个点
Point up = cur;
up.row--;
GetCycleShort(maze, up, pre, entry, cur_path, short_path);
Point right = cur;
right.col++;
GetCycleShort(maze, right, pre, entry, cur_path, short_path);
Point down = cur;
down.row++;
GetCycleShort(maze, down, pre, entry, cur_path, short_path);
Point left = cur;
left.col--;
GetCycleShort(maze, left, pre, entry, cur_path, short_path);
//如果四个方向都探测过了,就出栈回溯
SeqStackPop(cur_path);
return;
}