求带环的多出口迷宫的最短路径

在前面的博客中,我们已经解决了简单迷宫的求解问题以及多出口(不带环)迷宫求最短路径的问题。求带环的多出口迷宫的最短路径,需要我们在之前解决问题的思想上做出调整和改进。

  • 首先,我们先来定义一个带环的多出口迷宫:

                  

图中黄色标记为一个环,0表示墙,即无法落脚;1表示可以落脚;之后我们会用2来标记走过的路。

我们默认出口为四个边界上的点。若入口也为边界点,则出口与入口不能重合。

  • 解题思路的改进:

(1)CanStay函数。在之前的代码中,我们判断当前点是否能落脚,实现判断当前点是否在边界外,若在边界外则不能落脚;若在边界内且当前点的值为1,则可以落脚。但在求带环的多出口迷宫的最短路径时,我们还需要判断cur_value是否大于pre_value+1,若满足则应该落脚,若不满足则不应该落脚。

(2)Mark函数。在之前的代码中,我们将走过的路径点的值全部改为2,表示这个点已经走过。但在求带环的多出口迷宫的最短路径的问题时,我们不能简单的将走过的路径的点全部标记为一个特定的值。

                          

如上图所示,根据我们定义的按顺时针方向探测相邻点,那么该迷宫找到的第一条路径应该如图中蓝色数字标示所示。当找到一条路径后,进行出栈回溯,继续寻找下一条路径。

                        

此时就又新找到了一条路径。然后进行出栈回溯,继续新一轮的探测:

                        

当按顺时针方向进行探测时,走到图中红色圈出来的点后,就会发现,此时周围相邻的四个点要么无法落脚,要么已经走过,所以进行出栈回溯操作,继续寻找下一条路径。

                        

当回溯到上一个点后,按照顺时针探测的规则,下一个要走的点即为图中圈出来的点,当走到下一个点时,就相当于又找到了一条路径。相同地,进行出栈回溯,寻找下一条路径。此时会回溯到入口点,然后进行新一轮的探测。

                        

当回到入口点下面这个点时,向下进行探测时,发现下一个点已经被标记过,而右边的点不能落脚,所以探测就结束了。然而图中我们圈出来的这条路径,却没有被找到!

这就说明,在求带环的多出口迷宫的最短路径上,我们不能单纯地将已经走过的点标记为同一个特定值,而是应该采用新的方法:我们规定,将能落脚的入口点标记为2,之后的每个点都为上一个点的值+1,这样我们就得到了第一条路径的标记图:

                        

然后出栈回溯,寻找第二条路径:

                        

此时就找到了第二条路径。出栈回溯,继续寻找下一条路径:                        

                        

当走到图中标记为10的位置时,探测发现周围四个相邻点的值。探测上边的点时,发现3>10+1这个条件不满足,所以不应该落脚;探测右边和左边的点值为0,不能落脚,所以回溯到标记点为9的位置,继续新一轮的探测,发现当前位置左边的点满足标记条件,则直接落脚,并且此时又找到了一条路径。

                        

然后进行出栈回溯,回到标记为3的位置,按照顺时针方向,向下探测,发现10>3+1,所以可以落脚,并且将值改为4,之后的步骤同理:

                        

当走到图中圈出来的点时,向上探测,发现值为0不能落脚;向左探测发现7>6+1不成立,所以不能落脚;向下探测发现9>6+1成立,所以往下走,并修改标记值:

                        

这是就又找到了一条新的路径。以此方法类推,便能找到所有的路径。

(3)就如求解多出口迷宫(不带环)问题一样,由于多出口迷宫存在着多条路径,所以我们需要定义两个栈,一个栈中存放着当前走过的路径的数据,另一个栈中存放着最短路径的数据。每找到一条路径,就将当前路径的长度与最短路径的长度作比较,若存放最短路径的栈为空栈,或者当前路径的长度小于最短路径的长度,就用存放着当前路径的栈中的数据替代存放最短路径的栈中的数据。当所有路径都找到以后,此时存放最短路径的栈中的数据即可表示该迷宫的最短路径。

maze.h:
#pragma once
#include <stdio.h> 
#include<stdlib.h>
#include<stddef.h>

#define MAX_ROW 6
#define MAX_COL 6

typedef struct Point{
    int row;
    int col;
}Point;
typedef Point SeqStackType;

typedef struct SeqStack{
    SeqStackType *data;
    size_t size;
    size_t capacity;
}SeqStack;
typedef struct Maze{
    int map[MAX_ROW][MAX_COL];
}Maze;
maze.c:
#include "maze.h"
 
int map[MAX_ROW][MAX_COL]={
    {0,1,0,0,0,0},
    {0,1,1,1,0,0},
    {0,1,0,1,1,1},
    {1,1,1,1,0,0},
    {0,0,1,0,0,0},
    {0,0,1,0,0,0}
};

void MazeInitShortPathWithCycle(Maze* maze){
    if(maze == NULL)
        return;
    size_t i = 0;
    for(;i < MAX_ROW;i++){
        size_t j = 0;
        for(;j < MAX_COL;j++){
            maze->map[i][j] = map[i][j];
        }
    }
    return;
}

void SeqStackDebugPrint(SeqStack* stack,const char* msg){
    printf("%s\n",msg);
    if(stack == NULL)
        return;
    size_t i = 0;
    for(;i < stack->size;i++){
        printf("(%d,%d)\n",stack->data[i].row,stack->data[i].col);
    }
        printf("\n");
        return;
}

void MazePrint(Maze* maze){
    if(maze == NULL)
        return;
    size_t i = 0;
    for(;i < MAX_ROW;i++){
        size_t j = 0;
        for(;j < MAX_COL;j++)
            printf("%2d ",maze->map[i][j]);
        printf("\n");
    }
    return;
}
    
int CanStayWithCycle(Maze* maze,Point cur,Point pre){
    if(maze == 0)
        return 0;
    //判断当前点是否在地图上
    if(cur.row < 0 || cur.row >= MAX_ROW || cur.col < 0 || cur.col >= MAX_COL){
        return 0;
    }
    //判断是否为墙
    int cur_value = maze->map[cur.row][cur.col];
    int pre_value = maze->map[pre.row][pre.col];
    if(cur_value == 0){
        return 0;
    }
    //若当前点是1,可直接落脚
    if(cur_value == 1)
    return 1;
    //若当前点已走过,比较cur_value和pre_value的大小关系
    //若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){
        //单独考虑非法点的情况
        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;
}

int IsExit(Maze* maze,Point cur,Point entry){
    (void)maze;
    //1.判断当前点是不是入口,若为入口,则不是出口
    if(cur.row == entry.row && cur.col == entry.col){
        return 0;
    }
    //2.如果当前点在地图边界上,说明是出口
    if(cur.row == 0 || cur.row == MAX_ROW-1 || cur.col == 0 || cur.col == MAX_COL-1){
        return 1;
    }
    return 0;
}

void SeqStackAssgin(SeqStack* from,SeqStack* to){
    //释放to中的原有内存
    SeqStackDestroy(to);
    //根据from中的元素个数确定内存申请的大小,给to重新申请一个足够的内存
    to->size = from->size;
    to->capacity = from->capacity;
    to->data = (SeqStackType*)malloc(to->capacity * sizeof(SeqStackType));
    //再进行数据拷贝
    size_t i = 0;
    for(;i < from->size;i++){
        to->data[i] = from->data[i];
    }
}

void _GetShortPathWithCycle(Maze* maze,Point cur,Point pre,Point entry,SeqStack* cur_path,SeqStack* short_path){
    printf("cur:(%d,%d)\n",cur.row,cur.col);
    //1.判断当前点能否落脚
    if(!CanStayWithCycle(maze,cur,pre)){
        return;
    }
    //2.若能落脚,给当前位置做一个标记
    //同时将当前点插入到cur_path
    MarkWithCycle(maze,cur,pre);
    SeqStackPush(cur_path,cur);
    //3.若当前点为出口,说明找到了一条出口
    if(IsExit(maze,cur,entry)){
        printf("找到了一条路径\n");
        if(cur_path->size < short_path->size || short_path->size == 0){
            //将当前路径与short_path中的路径对比,若当前路径比short_path短或short_path本身为空栈,则用当前路径替换short_path
            SeqStackAssgin(cur_path,short_path);
            printf("找到了一条相对较短的路径\n");
        }
        //若当前路径没有比short_path短,就尝试找其他路径
        SeqStackPop(cur_path);
        return;
    }
    //4.若当前点不是出口,则按顺时针方向探测四个相邻的点,递归式调用函数自身,递归式更新cur节点
    //(每次递归时,cur都是下一次要走的点,这个点能否落脚,交给递归函数作判断)
    Point up = cur;
    up.row -= 1;
    _GetShortPathWithCycle(maze,up,cur,entry,cur_path,short_path);

    Point right = cur;
    right.col += 1;
    _GetShortPathWithCycle(maze,right,cur,entry,cur_path,short_path);

    Point down = cur;
    down.row += 1;
    _GetShortPathWithCycle(maze,down,cur,entry,cur_path,short_path);

    Point left = cur;
    left.col -= 1;
    _GetShortPathWithCycle(maze,left,cur,entry,cur_path,short_path);
    
    //若四个方向都递归的探测过了,则可进行出栈,同时回溯到上一个点
    SeqStackPop(cur_path);
    return;
}

void GetShortPathWithCycle(Maze* maze,Point entry){
    SeqStack cur_path;
    SeqStack short_path;
    SeqStackInit(&cur_path);
    SeqStackInit(&short_path);
    Point pre = {-1,-1};
    _GetShortPathWithCycle(maze,entry,pre,entry,&cur_path,&short_path);
    SeqStackDebugPrint(&short_path,"最短路径为");
    return;
}
test.c:
#include "maze.h"
#define PRINT_HEADER printf("\n============%s============\n",__FUNCTION__);

void Test(){
    PRINT_HEADER;
    Maze maze;
    SeqStack stack;
    MazeInitShortPathWithCycle(&maze);
    Point entry = {0,1};
    GetShortPathWithCycle(&maze,entry);
    MazePrint(&maze);
}
int main(){
    Test();
    return 0;
}

结果演示:

                    




猜你喜欢

转载自blog.csdn.net/cecilia3333/article/details/79974758