3、迷宮解決
4、迷宮遊戲(完整**)
傳送門:
《全國大學生計算機技能應用(2023年)——c++科目決賽程式設計題解》
迷宮問題簡介:現有乙個n * n的方格迷宮圖,相當於二維陣列,初始化中值為2表示█(牆壁),值為0表示可走空地。現在給定入口位置(starti,startj),出口位置(endi,endj)設計一種演算法,演算法中,當到達迷宮出口時,輸出走出迷宮的通路;若未到達迷宮出口,並且存在可走路徑即迷宮中為" "時,依次進行各個方向上的探索,直到找到將所有可能的結果試探完為止。如下圖:
//給出的迷宮圖,migo[1][0]為出發點,migo[7][8]為出口點
int migo[9]
[9]=
,,,,
,,,,
};
右上面進行圖形化迷宮就像這樣:
從圖中我們很直觀就看到走出迷宮的路徑,以下座標點為簡化表示,可對應陣列。出發點是(1,0),出口點(7,8)。
既然這樣,如何實現演算法找到這一條路呢?
我們看看題目,發現要求是找出迷宮通路,沒有其它約束或者額外的要求,目的明確,只要找到通路,對於不同迷宮圖,存在的通路可能不止一條。如此甚好,我們將給定的出發點視為根結點,以此搜尋子樹,對每個子通道進行深度優先搜尋,找到乙個到達目的點通路時,回溯到根再次搜尋問題解。
經過對問題解一番分析之後,不難發現,符合回溯法解題框架:遞迴回溯
(1)基本思想
回溯法基本思想:回溯法有「通用的解題法」之稱,可以系統搜尋乙個問題的所有解或者任一解,既帶有系統性又帶有跳躍性的搜尋演算法。
回溯法求問題所有解時候,要回溯到根,且此時根結點的所有子樹都已經被搜尋過才結束。
回溯法求問題乙個解時,只要搜尋到問題的乙個解就可以結束。
(2)子集樹與排列樹
剛剛提到的解空間樹,常見的兩類是子集樹和排列數。
如何來區分呢?
劃重點a.當所給問題是從n個元素的集合s中找出滿足某種性質的子集時,相應的解空間樹為子集樹。
b.當所給問題是確定n個元素滿足某種性質的排列時,其解空間為排列樹。
//回溯法子集樹一般演算法
void
backtrack
(int t)
}}
//回溯法排列樹一般演算法
void
backtrack
(int t)
}}
典型例子:
0-1揹包問題解空間就是子集樹,它是從n個物品中選擇滿足要求並且價值最大的子集。
旅行售貨員問題解空間就是排列樹,它是確定n個地點找到最佳路徑的乙個排列。
以上演算法知識的了解和題目解析之後,回頭看迷宮問題,它就是乙個很好的可以使用回溯演算法解決的問題,而且可知解空間樹為子集樹,根據上面的說法,換言之,我們要從n個可走的點找到乙個子集,作為走出迷宮的通路!
(1)遞迴出口
解題過程:
定義陣列int way_value[9][9];
來設定乙個權值,用來判斷迷宮的方向,指定1代表→,2代表↓,3代表← ,4代表向↑。
運用回溯子集樹演算法思想,我們的函式宣告為void visit(int i,int j,int way)
,( i , j )為當前所在位置,way的值表示前進方向。首先,問題解的輸出,搜尋迷宮路徑相對應為:
/*
此為遞迴出口,如果一步步試探成功,即到達迷宮出口,則輸出迷宮圖"█"及路徑"→↓←↑"
*/if(i==endi&&j==endj)
//判斷有沒有到到達迷宮出口,列印出所有走出迷宮的方法
else
cout<<
" ";}
cout<
}}
(2)確定回溯方法
問題關鍵在於遞迴回溯的函式體部分,我們猜想,如果進入函式中,判斷傳值的點是否可走,並以此為根結點來搜尋子樹,那如何構造這個部分的函式?
我想到的方法是,既然要確定下一步走的方向,那就判斷當前點的上下左右4個方向是否訪問過並且可走,對可走的方向進一步搜尋前進方向,否則,不對此結點進一步搜尋。
//判斷當前位置右邊4個方向,如果未訪問過,則標記way_values的值,遞迴
if(migo[i]
[j+1]==
0)//判斷右
visit
(i,j+1,
1);//對該點下一步搜尋,way=1標記方向為右
if(migo[i+1]
[j]==0)
//判斷左,以此類推
visit
(i+1
,j,2);
if(migo[i]
[j-1]==
0)visit
(i,j-1,
3);if
(migo[i-1]
[j]==0)
visit
(i-1
,j,4
);
此步驟可見得,只實現了遞迴,根據上面講到的回溯思想和演算法,搜尋結束,要回溯根結點,需要回溯上乙個狀態,所以加上migo[i][j]=0;//恢復狀態
即可。
演算法完整**如下:
void
visit
(int i,
int j,
int way)
else
cout<<
" ";}
cout<
//判斷當前位置右邊4個方向,如果未訪問過,則標記way_values的值,遞迴
if(migo[i]
[j+1]==
0)//判斷右
visit
(i,j+1,
1);//對該點下一步搜尋,way=1標記方向為右
if(migo[i+1]
[j]==0)
//判斷左,以此類推
visit
(i+1
,j,2);
if(migo[i]
[j-1]==
0)visit
(i,j-1,
3);if
(migo[i-1]
[j]==0)
visit
(i-1
,j,4);
migo[i]
[j]=0;
//恢復狀態
}
迷宮問題完整程式:
#include
using
namespace std;
//初始化迷宮,點為2表示迷宮圖為"█",點為0表示迷宮圖為" "
int migo[9]
[9]=
,,,,
,,,,
};//建立出迷宮圖
int way_value[9]
[9];
//設定乙個權值,用來判斷迷宮的方向
int starti=
1,startj=0;
//設定出發點為1,0
int endi=
7,endj=8;
//出口為7,7
/*遞迴回溯演算法:
演算法中,當到達迷宮出口時,輸出走出迷宮的通路;
若未到達迷宮出口,並且存在可走路徑即迷宮中為" "時,依次進行
各個方向上的探索,直到找到將所有可能的結果試探完為止。
*/void
visit
(int i,
int j,
int way)
else
cout<<
" ";}
cout<
//判斷當前位置右邊4個方向,如果未訪問過,則標記way_values的值,遞迴
if(migo[i]
[j+1]==
0)//判斷右
visit
(i,j+1,
1);//對該點下一步搜尋,way=1標記方向為右
if(migo[i+1]
[j]==0)
//判斷左,以此類推
visit
(i+1
,j,2);
if(migo[i]
[j-1]==
0)visit
(i,j-1,
3);if
(migo[i-1]
[j]==0)
visit
(i-1
,j,4);
migo[i]
[j]=0;
//恢復狀態
}/********
main函式:
首先顯示給出的迷宮圖,然後呼叫visit函式,對迷宮進行探索
********/
intmain()
memset
(way_value,0,
sizeof
(way_value));
//初始化way_value陣列全部為0
cout<<
"迷宮路徑如下:"
;visit
(starti,startj,1)
;return0;
}
使用遞迴回溯解決迷宮問題
目的 從左上角走到右下角。地圖用陣列表示,0表示可走,1表示牆,2表示已走過 不能再走 3表示已走過但走不通。策略 向下 向右 向上 向左 初始地圖 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 1 1 0 0...
遞迴演算法 迷宮回溯問題
package com.czn.recursion public class migong for int i 0 i 8 i map 3 1 1 map 3 2 1 for int i 0 i 8 i system.out.println boolean setway setway map,1,1...
迷宮問題讓你深度理解遞迴 回溯)
當你看到遞迴時,如果腦子裡想著迴圈,一層層向下呼叫,一層層回溯,總想著計算機的每一步是怎麼做的,這樣就會陷入學習遞迴的思維誤區 正確的做法是什麼呢?遮蔽遞迴細節 假設a問題,可以細分為bcd這三個小問題,那麼我們就應該考慮bcd這三者怎麼解決,然後能解決之後再考慮bcd和a的關係 例 如果要解決我坐...