回溯法解決迷宮問題

龍躍十二發表於2018-02-08

今天在又一次看到了回溯法解決關於迷宮的問題,於是在這裡分享給大家。

回溯法:對一個包括有很多個結點,每個結點有若干個搜尋分支的問題,把原問題分解為若干個子問題求解的演算法;當搜尋到某個結點發現無法再繼續搜尋下去時,就讓搜尋過程回溯(回退)到該節點的前一個結點,繼續搜尋該節點外的其他尚未搜尋的分支;如果發現該結點無法再搜尋下去,就讓搜尋過程回溯到這個結點的前一結點繼續這樣的搜尋過程;這樣的搜尋過程一直進行到搜尋到問題的解或者搜尋完了全部可搜尋分支沒有解存在為止。
迷宮問題用這種方法簡直很完美,當我們尋找通路時,當前方無法通過返回到上一層繼續搜尋其他沒有搜尋的通路,就這樣一層一層的找通路,最終找到出口,還有一種情況就是迷宮本來就沒有通路,只能是返回到起點。當然還涉及到多條通路時我們如何找到最短通路。
回溯過程如下圖所示:
回溯法求解迷宮問題
0表示牆壁,1表示通路,函式引數傳入迷宮起點,每次向前一步則把前一步設定為2,防止再次試探該位置。
本題我用迭代法和遞迴法分別實現了找出迷宮多條通路問題
迭代法程式碼:

void SearchMazePath(Pos entry) //求取通路 回溯法(迭代)
{
    Stack *s;
    Pos next = entry;
    PushStack(&s,next);
    do
    {
        Maze[next._x][next._y] = 2;
        next = TopStack(s);
        next._x += 1; //if (CheckCoord(next)) 
        {
            PushStack(&s,next);
            continue;
        }

        next = TopStack(s);
        next._x -= 1;//if (CheckCoord(next))
        {
            PushStack(&s,next);
            continue;
        }

        next = TopStack(s);
        next._y += 1;//if (CheckCoord(next))
        {
            PushStack(&s,next);
            continue;
        }

        next = TopStack(s);
        next._y -= 1;//if (CheckCoord(next))
        {
            PushStack(&s,next);
            continue;
        }

        PopStack(&s);  //改層沒有通路,則pop掉棧頂,返回到上一層繼續試探其他方向
        next = TopStack(s);
    }while (EmptyStack(s) && next._x != entry._x);
}

遞迴法實現程式碼:

void SearchMazePathR(Pos entry) //求取通路(遞迴)
{
    Pos next = entry;
    Maze[next._x][next._y] = 2;

    next = entry;
    next._x -=1;
    if (CheckCoord(next))
        SearchMazePathR(next);

    next = entry;
    next._x +=1;
    if (CheckCoord(next))
        SearchMazePathR(next);

    next = entry;
    next._y -=1;
    if (CheckCoord(next))
        SearchMazePathR(next);

    next = entry;
    next._y +=1;
    if (CheckCoord(next))
        SearchMazePathR(next);
}

條件判斷補充程式碼:

int CheckCoord(Pos pos)
{
    if (pos._x >= 0 && pos._x<ROW 
        && pos._y >=0 && pos._y<COL  
        && Maze[pos._x][pos._y] == 1)
    {
        return 1;
    }
    else
        return 0;
}

執行結果:
這裡寫圖片描述

尋找最短通路:
此時思路還是利用回溯,一步一步試探通路,但是我們需要修改標記方法。當我們向下一個位置試探時發現該位置可通就把該位置標記為比前一個位置大一的數,當下一個位置是1或者比原位置大的數則確定為通路。當下一個位置是0或者比原位置小的數則確定通。這樣當我們遍歷完所有通路即可得到最短通路。
先看看試探通路的結果:
這裡寫圖片描述
明顯可以看出遍歷了四條路,最短通路為10步。
程式碼如下:

void SearchShortPathR(Pos entry,Pos cur) //尋找最短路徑
{
    Pos next = entry;
    Pos prev = cur;
    Maze[next._x][next._y] = Maze[prev._x][prev._y] + 1;
    if (next._y == COL-1)
        printf("Exit:(%d ,%d)\n",next._x,next._y);  //列印出口

    next = entry;
    next._x += 1;
    if (CheckCoordS(next,entry))
        SearchShortPathR(next,entry);

    next = entry;
    next._x -= 1;
    if (CheckCoordS(next,entry))
        SearchShortPathR(next,entry);

    next = entry;
    next._y += 1;
    if (CheckCoordS(next,entry))
        SearchShortPathR(next,entry);

    next = entry;
    next._y -= 1;
    if (CheckCoordS(next,entry))
        SearchShortPathR(next,entry);
}

總結回溯法解題通常包含以下三個步驟:

1.針對所給問題,定義問題的解空間;
2.確定易於搜尋的解空間結構;
3.以深度優先方式搜尋解空間,並在搜尋過程中用必要的條件避免無效搜尋。

相關文章