迷宮---多出口迷宮的最短路徑

如圖是一個有三個出口的迷宮,現在來求它的最短路徑(不帶環)
這裏寫圖片描述
思想:定義兩個棧,一個棧存放當前路徑。一個棧存最短路徑,每次都將當前路徑和最短路徑比較如果當前路徑小於最短路徑,那就交換兩個棧中的元素
代碼實現:

void MazeInit2(Maze* maze)
{
    if(maze == NULL)
    {
       return;
    }
    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,0,0,0},
        {0,0,1,0,0,0},
        {0,0,1,0,0,0}
    };
    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 _GetShortPath(Maze* maze,Point cur,Point entry,SeqStack* cur_path,SeqStack* short_path)
{
    //1.判斷當前是否能落腳
    if(!CanStay(maze,cur))
    {
        return;
    }
    //2.如果能落腳,就對當前點進行標記,同時把當前點插入到cur_path
    Mark(maze,cur);
    SeqStackPush(cur_path,cur);
    //判斷當前點是否是出口
    if(IsExit(maze,cur,entry))
    {
        //a)如果是出口,說明找到了一條路徑,拿當前路徑和short_path中的路徑進行對比
        //,如果當前路徑比short_path短,或short_path本身是一個空棧
        //  就用當前路徑代替short_path
        printf("找到一條路經!\n");
        if(cur_path->size < short_path->size || short_path->size == 0)
        {
            printf("找到一條比較短的路徑!\n");
            SeqStackAssgin(cur_path,short_path);
        //printf("cur :%d,%d\n",cur.row,cur.col);
        }
            //b)如果當前路徑沒有short_path短,就繼續再找其他路徑(進行回溯)
            //  回溯之前要把cur_path棧頂元素出棧
        SeqStackPop(cur_path);
        return;
    }
    //4.如果當前點不是出口,嘗試探測四個方向(按照順時針的方向探測)
    Point up = cur;
    up.row -= 1;
    _GetShortPath(maze,up,entry,cur_path,short_path);

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

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

    Point left = cur;
    left.col -= 1;
    _GetShortPath(maze,left,entry,cur_path,short_path);
    //5.如果四個方向都探測過了,就可以進行出棧(指當前函數棧幀結束
    //,同時cur_path,也要進行出棧),回溯到上一個點
    SeqStackPop(cur_path);
    return;
}
//找到所有路徑,然後從所有的路徑中篩選出一條最短的(遞歸)
void GetShortPath(Maze* maze,Point entry)
{
    //就像從一個數組中查找最小元素,一樣,定義一個short_path保存最短路徑
    SeqStack cur_path;//保存當前路徑
    SeqStack short_path;//保存最短路徑
    SeqStackInit(&cur_path);
    SeqStackInit(&short_path);
    _GetShortPath(maze,entry,entry,&cur_path,&short_path);
    SeqStackDebugPrint(&short_path,"最短路徑");
}

seqstack.c中的代碼實現

#ifdef FOR_MAZE
//#include<stdio.h>
//#include"seqstack.h"
//此函數僅用於迷宮問題,用來調試
//通常意義下,棧是不允許遍歷的
//但是如果進行調試或者測試,就不一樣了
//因此再這裏雖然函數進行遍歷了,但是僅用於調試
//之所以寫這個函數遍歷棧,是爲了能夠從入口到出口的順序來打印棧的內容
void SeqStackDebugPrint(SeqStack* stack,const char* msg)
{
    printf("[%s]\n",msg);
    size_t i = 0;
    for(;i < stack->size ; ++i)
    {
        printf("(%d,%d)\n",stack->data[i].row,stack->data[i].col);
    }
    printf("\n");
}
void SeqStackAssgin(SeqStack* from,SeqStack* to)
{
    //採用下面的方法保證to裏面的內存能夠足夠容納from中的元素
    //1.釋放to中的原有內存
    SeqStackDestroy(to);
    //2.根據from元素的個數,確定內存申請內存的大小,給to重新申請一個足夠的內存
    to->size = from->size;
    to->capacity = from->capacity;
    to->data = (SeqStackType*)malloc(to->capacity * sizeof(SeqStackType));
    //3.進行數據拷貝
    size_t i = 0;
    for(;i < from->size ; ++i)
    {
        to->data[i] = from->data[i];
    }
    return;
}
#endif

測試代碼

void Test3()
{
    TEST_HEADER;
    Maze maze;
    MazeInit2(&maze);
    Point entry = {0,1};
    GetShortPath(&maze,entry);
    MazePrint(&maze);
}

運行結果:
這裏寫圖片描述

如圖是一個有三個出口的迷宮,現在來求它的最短路徑(帶環)
這裏寫圖片描述
因爲這個迷宮帶環路徑較多就不一一畫出來了
第一次標記的結果
這裏寫圖片描述
第二次標記的結果
這裏寫圖片描述
第三次標記的結果
這裏寫圖片描述
第四次標記結果
這裏寫圖片描述
第五次標記結果
這裏寫圖片描述
具體看代碼實現

void MazeInit3(Maze* maze)
{
    if(maze == NULL)
    {
       return;
    }
    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}
    };
    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;
}   
int CanStayWithCycle(Maze *maze,Point cur,Point pre)
{
    if(maze == NULL)
    {
        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];
    if(cur_value == 0)
    {
        return 0;
    }
    //如果當前點是1,就可以直接落腳
    if(cur_value == 1)
    {
        return 1;
    }
    //當前點若已經被標記就比較當前點對應的值和pre對應的值的大小
    //a)cur_path 7,pre_value 5,應該落腳
    //b)cur_path 6,pre_value 5,不應該落腳
    //c)cur_path 5,pre_value 5,不應該落腳
    //d)cur_path 4,pre_value 5,不應該落腳
    //總結以上的幾種情況可以總結出:cur_path > pre_path + 1就應該落腳
    int pre_value = maze->map[pre.row][pre.col];
    if(cur_value > pre_value+1)
    {
        return 1;
    }
    return 0;
}

void MarkWithCycle(Maze* maze,Point cur,Point pre)
{
    if(maze == NULL)
    {
        return;
    }
    if(cur.row < 0 || cur.row >= MAX_ROW || cur.col < 0 || cur.col >= MAX_COL)
    {
        return;
    }
   // if(pre.row < 0 || pre.row >= MAX_ROW || pre.col < 0 || pre.col >= MAX_COL)
   // {
   //     return;
   // }
    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;
    return;
}

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);
    if(!CanStayWithCycle(maze,cur,pre))
    {
        return;
    }
    //標記當前點,並且將當前點插入到cur_path
    MarkWithCycle(maze,cur,pre);
    SeqStackPush(cur_path,cur);
    pre = cur;
    //判斷當前點是否是出口
    if(IsExit(maze,cur,entry))
    {
        printf("找到了一條路徑!\n");
        //如果是出口,要拿cur_path和short_path進行比較,把比較短的路徑保存到short_path中
        if(cur_path->size < short_path->size || short_path->size == 0)
        {
            printf("找到了一條比較短的路徑!\n");
            SeqStackAssgin(cur_path,short_path);
        }
        //進行回溯,不管當前找到的這條路徑是否是比較短的路徑,都要進行回溯
        SeqStackPop(cur_path);
        return;
    }
    //如果不是出口,以當前點爲基準點,探測周圍的四個點
    Point up = cur;
    up.row -= 1;
    _GetShortPathWithCycle(maze,up,pre,entry,cur_path,short_path);
    Point right = cur;
    right.col += 1;
    _GetShortPathWithCycle(maze,right,pre,entry,cur_path,short_path);
    Point down = cur;
    down.row += 1;
    _GetShortPathWithCycle(maze,down,pre,entry,cur_path,short_path);
    Point left = cur;
    left.col -= 1;
    _GetShortPathWithCycle(maze,left,pre,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,"最短路徑爲");
}

測試代碼

void Test4()
{
    TEST_HEADER;
    Maze maze;
    MazeInit3(&maze);
    Point entry = {0,1};
    GetShortPathWithCycle(&maze,entry);
    MazePrint(&maze);
}

運行結果
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章