7-12 求迷宮最短通道
(整合版,程式碼非原創)
分數 10
作者 C專題課程組-humin
單位 浙江大學
遞迴求解迷宮最短通道的總步長。輸入一個迷宮,求從入口通向出口的可行路徑中最短的路徑長度。為簡化問題,迷宮用二維陣列int maze[10][10]來儲存障礙物的分佈,假設迷宮的橫向和縱向尺寸的大小是一樣的,並由程式執行讀入, 若讀入迷宮大小的值是n(3<n<=10),則該迷宮橫向或縱向尺寸都是n,規定迷宮最外面的一圈是障礙物,迷宮的入口是maze[1][1],出口是maze[n-2][n-2], 若maze[i][j] = 1代表該位置是障礙物,若maze[i][j] = 0代表該位置是可以行走的空位(0<=i<=n-1, 0<=j<=n-1)。求從入口maze[1][1]到出口maze[n-2][n-2]可以走通的路徑上經歷的最短的總步長。要求迷宮中只允許在水平或上下四個方向的空位上行走,走過的位置不能重複走。
輸入格式:
輸入迷宮大小的整數n, 以及n行和n列的二維陣列(陣列元素1代表障礙物,0代表空位)
輸出格式:
若有可行的通道則輸出一個整數,代表求出的通道的最短步長;若沒有通道則輸出"No solution"
輸入樣例:
10
1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 1 0 1
1 0 0 1 0 0 0 1 0 1
1 0 0 0 0 1 1 0 0 1
1 0 1 1 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1
1 0 1 0 0 0 1 0 0 1
1 0 1 1 1 0 1 1 0 1
1 1 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1
上述輸入代表的是如下這樣一個迷宮:
其中紅色的小方塊是障礙物,藍色的小方塊是空位,白色的小圓連起來是一條從入口到出口的通道,兩個圓之間代表一個步長。
輸出樣例:
14
迷宮問題常用:深度優先搜尋演算法(DFS),下面給出兩段運用DFS的參考程式碼
參考程式碼1
採用了遞迴的方式來搜尋迷宮的路徑,透過不斷地向四個方向進行搜尋,並使用一個全域性變數來記錄最短路徑的長度。這種方法比較直觀,容易理解,但是可能會在處理大型迷宮時出現棧溢位的問題。
#include<stdio.h>
int road[12][12],apr[12][12],N,minRoad=9999;//全域性變數
void setWay(int x,int y,int *result,int deep)
{
//找到出口
if(x==N-1 && y==N-1)
{
*result = *result > deep ? deep : *result;
return;
}
//判斷該點是否經過
if(road[x][y] || apr[x][y]) return;
//假設沒有經過
apr[x][y] = 1;
setWay(x-1,y,result,deep+1);
setWay(x+1,y,result,deep+1);
setWay(x,y+1,result,deep+1);
setWay(x,y-1,result,deep+1);
apr[x][y] = 0;
}
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
for(int j=1;j<=N;j++)
{
scanf("%d",&road[i][j]);
}
}
setWay(2,2,&minRoad,0);
//找不到出口
if(minRoad==9999) printf("No solution");
else printf("%d",minRoad);
return 0;
}
參考程式碼2
採用了遞迴+回溯法來解決問題,透過遞迴遍歷所有可能的路徑,並記錄下最短路徑的長度。這種方法相對來說更加靈活,可以很好地處理各種大小的迷宮,但是程式中的一些判斷條件可能略顯冗餘。
#include<stdio.h>
int a,b[10][10],min=0,x[10][10],num=0,l=0;
//x陣列相當於bool陣列,記錄該方格是否被走過
//num記錄所走的步數
//l記錄所走的路線
//min記錄路線最
void lemon(int m,int n)
{
if(m==a-2 && n==a-2)//如果函式到達了終點,記錄其走過的長度
{
if(l==0)//記錄第一次走到終點路線的長度,將他賦值給min。
{
min=num;
}
if(num<min)//將走到終點的長度與min來對比
{
min=num;
}
l++;//走到終點路線條數
return;
}
//接下來是移動方位控制
if(b[m][n]==0 && x[m][n]==0)//向上走
{
num++;//記錄走過的步數
x[m][n]=1;//將走過的方位標記,防止下一次移動重複。
lemon(m+1,n);//向上走
//如果執行到這裡,說明前面所走的路都不通或者已經走到終點退回來,這時候就將所走的方位標記重置,並且走過的步數減1
x[m][n]=0;//將這次方位標記
num--;
}
if(b[m][n]==0 && x[m][n]==0)//向下走
{
num++;//記錄走過的步數
x[m][n]=1;//將走過的方位標記,防止下一次移動重複。
lemon(m-1,n);//向上走
//如果執行到這裡,說明前面所走的路都不通或者已經走到終點退回來,這時候就將所走的方位標記重置,並且走過的步數減1
x[m][n]=0;//將這次方位標記
num--;
}
if(b[m][n]==0 && x[m][n]==0)//向右走
{
num++;//記錄走過的步數
x[m][n]=1;//將走過的方位標記,防止下一次移動重複。
lemon(m,n+1);//向上走
//如果執行到這裡,說明前面所走的路都不通或者已經走到終點退回來,這時候就將所走的方位標記重置,並且走過的步數減1
x[m][n]=0;//將這次方位標記
num--;
}
if(b[m][n]==0 && x[m][n]==0)//向左走
{
num++;//記錄走過的步數
x[m][n]=1;//將走過的方位標記,防止下一次移動重複。
lemon(m,n-1);//向上走
//如果執行到這裡,說明前面所走的路都不通或者已經走到終點退回來,這時候就將所走的方位標記重置,並且走過的步數減1
x[m][n]=0;//將這次方位標記
num--;
}
}
int main()
{
scanf("%d",&a);//輸入迷宮長度
for(int c=0;c<a;c++)//構造迷宮圖形
{
for(int d=0;d<a;d++)
{
scanf("%d",&b[c][d]);
}
}
lemon(1,1);//將入口座標傳給函式
if(min) //如果min值依然為0,說明一直都沒找到出口,輸出No solution,如果min大於0,說明已經找到最短路線,則輸出。
printf("%d",min);
else
printf("No solution");
}