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

问题引出

顾名思义,有多条出口的迷宫,迷宫内部各路径有交叉,成环,求其最短路径
例如:
多出口路径带环最短路径迷宫

解题思路

整体思路和多出口不带环迷宫最短路径求解一样,只是这次更改的是标记函数 Mark 与 判断是否能够落脚函数 Canstay
因为要判断路径是否带环,以前单一的将走过的路径标记为2已经不能满足我们的需求了
所以这次需要将走过的块标记为当前步数,即每块走过的位置都一个不同的标记,这样,我们就能识别出环路径了

对于多出口不带环迷宫最短路径求解不清楚的可以移步

多出口迷宫最短路径求解

方法实现

这里我们要用到栈的一些操作,对于栈的概念和操纵还不太理解的可以移步

顺序表实现的数据结构栈

maze.c

#include "seqstack.h"
#include <stdio.h>

// 迷宫的长宽
#define ROW 6 // 行
#define COL 6 // 列

typedef struct Maze {
    int map[COL][ROW];
} Maze;

// 创建迷宫
void createMaze(Maze* maze) {
    int tmp[COL][ROW] = {
        { 0, 1, 0, 0, 0, 0},
        { 1, 1, 1, 1, 1, 0},
        { 0, 1, 0, 0, 1, 0},
        { 0, 1, 0, 0, 1, 0},
        { 0, 1, 1, 1, 1, 1},
        { 0, 1, 0, 0, 0, 0},
    };
    int i = 0;

    for( ; i < COL; i++) {
        int j = 0;
        for( ; j < ROW; j++) {
            maze->map[i][j] = tmp[i][j];
        }
    }
}

// 打印迷宫
void printMaze(Maze maze) {
    int i = 0;

    // 装饰性打印
    printf(" +");
    for( ; i < ROW*2; i++) {
        printf("-");
    }
    printf("-+");
    printf("\n");

    // 打印开始
    for( i = 0; i < COL; i++) {
        int j = 0;
        printf(" | ");
        for( ; j < ROW; j++) {
            printf("%d ", maze.map[i][j]);
        }
        printf("|\n");
    }

    // 装饰性打印
    printf(" +");
    for( i = 0; i < ROW*2; i++) {
        printf("-");
    }
    printf("-+\n");

}

// 判断当前点是否能够落脚
// 传入新参数 当前点前一个点 pre
//  比较 pos 与 pre 两个点的值,来判断当前是否能够落脚
int Canstay(Maze* maze, Coordinates pos, Coordinates pre) {
    // 越界
    if(pos.row>=0 && pos.row<ROW && pos.col>=0 && pos.col<COL) {
        if(maze->map[pos.col][pos.row] == 0) {
            return 0;
        }
        // 当前位置还没有被标记过,可以落脚 
        if(maze->map[pos.col][pos.row] == 1) {
            return 1;
        }
        // 比较 pos 与 pre 两个点的值,来判断当前是否能够落脚
        //  pre = pos   不能落脚   
        //  pre > pos   不能落脚
        //  pre = pos-1 不能落脚  
        //  pre < pos   可以落脚
        if(maze->map[pos.col][pos.row] > maze->map[pre.col][pre.row]+1) {
            return 1;
        } else {
            return 0;
        }
    }
    return 0;
}

// 将当前点进行标记
// 标记为前一个点的值+1
void Mark(Maze* maze, Coordinates pos, Coordinates pre) {
    maze->map[pos.col][pos.row] = maze->map[pre.col][pre.row]+1;
}

// 是否为出口
int isExit(Maze* maze, Coordinates entry, Coordinates pos) {
    if((pos.row==entry.row) && (pos.col==entry.col)) {
        return 0;
    }
    if(pos.row==0 || pos.row==ROW-1 || pos.col==0 || pos.col==COL-1) {
        return 1;
    }
    return 0;
}

// 打印路径
void printPath(SeqStack stack) {
    Datatype value;
    while(SeqStackTop(&stack, &value)) {
        printf("(%d, %d)\n", value.row, value.col );
        SeqStackPop(&stack);
    }
}

// 比较两个栈长度,s1 <= s2,返回 <=0,否则, >0
int compareStack(SeqStack stack1, SeqStack stack2) {
    Datatype value;
    int ret = 0;
    int count1 = 0;
    int count2 = 0;
    while(ret=SeqStackTop(&stack1, &value)) {
        SeqStackPop(&stack1);
        count1++;
    }
    while(ret=SeqStackTop(&stack2, &value)) {
        SeqStackPop(&stack2);
        count2++;
    }
    return count1-count2;

} 

// 将 src 栈的所有元素拷贝到 dst 栈中 
void cloneStack(SeqStack* dest, SeqStack* src) {
    // 由于是顺序表构成的栈,所以首先释放 dest 空间
    SeqStackDestroy(dest);
    // 创建新空间
    dest->size = src->size;
    dest->capacity = src->capacity;
    dest->data = malloc(sizeof(SeqStack) * src->capacity);
    int i = 0;
    for(i = 0; i < src->size; i++) {
        dest->data[i] = src->data[i];
    }
}   

void getMazePath(Maze* maze, Coordinates entry) {
    // 构建两个栈,一个栈为 cur_path,保存当前走过的路径,另一个栈为 short_path,保存最短的路径
    SeqStack cur_path;
    SeqStack short_path;
    SeqStackInit(&cur_path);
    SeqStackInit(&short_path);
    // 判断当前该点是否能落脚,不能落脚直接返回 
    if(!Canstay(maze, entry, entry)) {
        return; 
    }
    // 标记当前点,并且入栈到cur_path
    Mark(maze, entry, entry);
    SeqStackPush(&cur_path, entry);
    int ret = 1;    
    while(ret == 1) {
        // 进入循环,先拿到当前的栈顶元素(该元素一定合法)
        Coordinates cur;
        ret = SeqStackTop(&cur_path, &cur);

        // 判断当前点是否为出口,是出口就找到了一条路 
        if(isExit(maze, entry, cur) == 1) {
            // 是出口,将 两个栈中的元素进行比较 
            // 如果short_path < cur_path,返回 <0,否则 >0
            int compare = compareStack(cur_path, short_path);
            // 第一次 short_path 为空
            Datatype value;
            if(SeqStackTop(&short_path, &value) == 0) {
                cloneStack(&short_path, &cur_path); 
                printf("找到一条路径[%d, %d]\n", cur.col, cur.row);
            }
            // 如果cur_path保存的路径比short_path中保存的路径长,就将当前 cur_path 路径 保存至short_path中 
            if(compare <  0) {
                cloneStack(&short_path, &cur_path); 
                printf("找到一条更短的路径[%d, %d]\n", cur.col, cur.row);

            } else {
                // 否则,继续寻找其他路径
                printf("又找到一条路径[%d, %d]\n", cur.col, cur.row);
            }
            // 出栈当前栈顶元素,回溯
            SeqStackPop(&cur_path);
        }       
        // 如果不是,按顺时针方向依次取相邻点,并判定他们是否能落脚,如果可以,入栈,进入下一轮循环
        // 上
        Coordinates up;
        up.col = cur.col-1;
        up.row = cur.row;

        if(Canstay(maze, up, cur)) {
            Mark(maze, up, cur);
            SeqStackPush(&cur_path, up);
            continue;
        }

        //  右 
        Coordinates right;
        right.col = cur.col;
        right.row = cur.row+1;

        if(Canstay(maze, right, cur)) {
            Mark(maze, right, cur);
            SeqStackPush(&cur_path, right);
            continue;
        }


        //  下 
        Coordinates down;
        down.col = cur.col+1;
        down.row = cur.row;

        if(Canstay(maze, down, cur)) {
            Mark(maze, down, cur);
            SeqStackPush(&cur_path, down);
            continue;
        }

        // 左 
        Coordinates left;
        left.col = cur.col;
        left.row = cur.row-1;

        if(Canstay(maze, left, cur)) {
            Mark(maze, left, cur);
            SeqStackPush(&cur_path, left);
            continue;
        }

        // 四个点都不能落脚,出栈当前栈顶元素,回溯
        SeqStackPop(&cur_path);
    }
    // 打印路径
    printPath(short_path);
}

int main() {
    Maze maze;
    Coordinates entry;
    entry.col = 0;
    entry.row = 1;
    // 创建迷宫
    createMaze(&maze);
    // 打印迷宫
    printMaze(maze);
    // 求解迷宫线路
    getMazePath(&maze, entry);

    printMaze(maze);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值