7-12 求迷宮最短通道(整合版)

yesno233233發表於2024-03-10

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");


}

相關文章