DFS 深搜專題 入門典例 -- 凌宸1642

凌宸00 發表於 2021-04-05

DFS 深搜專題 入門典例 -- 凌宸1642

深度優先搜尋 是一種 列舉所有完整路徑遍歷所有情況的搜尋方法 ,使用 遞迴 可以很好的實現 深度優先搜尋。

1 最大價值

題目描述

​ 有 n 件物品,每件物品的重量為 w[i] , 價值為 c[i] 。現在需要選出若干件物品放入一個容器為 V 的揹包中,使得在選入揹包的物品重量和不超過容量 V 的前提下 ,讓揹包中的物品的價值之和最大,求最大價值。(1 ≤ n≤ 20 )

輸入描述:

​ 第一行輸入物品總數 n 和 揹包容量 v 。

​ 第二行輸入 n 個整數, 分別表示 n 件物品各自的重量。

​ 第三行輸入 n 個整數, 分別表示 n 件物品各自的價值。

輸出描述:

​ 對於每個測試用例,在一行中輸出最大價值。

樣例輸入:

5 8
3 5 1 2 2
4 5 2 1 3

樣例輸出:

10
#include<bits/stdc++.h>
using namespace std ;
#define MAX 30
int n , v , maxValue = 0 ; // 物品件數 n,揹包容量 v,最大價值 maxValue 
int w[MAX] , c[MAX] ; // w[i]為每件物品的重量, c[i]每件物品的價值
// dfs ,index 表示當前處理物品的編號 , sumW sumC 分別為當前 總重量 和 總價值
void dfs(int index , int sumW , int sumC){
	if(index == n) { // 已經完成對 n 件物品的處理
		if(sumW <= v && sumC > maxValue ) maxValue = sumC ; // 如果此時最大價值符合題意,則更新
		return ;
	}
	// 岔道口 
	dfs(index + 1 , sumW , sumC) ; // 不選擇第 index 件物品
	dfs(index + 1 , sumW + w[index] , sumC + c[index]) ; // 選擇了第 index 件物品 
} 
int main(){
	cin >> n >> v ; // 輸入 物品數 n 和 揹包容量 v 
	for(int i = 0 ; i < n ; i ++) cin >> w[i] ; // 輸入 n 件物品的 重量
	for(int i = 0 ; i < n ; i ++) cin >> c[i] ; // 輸入 n 件物品的 價值
	dfs(0 , 0 , 0) ; // 呼叫 dfs 進行深搜
	cout << maxValue << endl ;  // 深搜結束後, maxValue 中存有最大價值,直接輸出
	return 0;
}
// 對此 dfs 進行剪枝後 ,剪枝是在保證演算法正確的情況下,通過題目條件限制來減少 DFS 計算量的方法
void dfs(int index , int sumW , int sumC){
    if(index == n) return ; //完成對 n 件物品的選擇
    dfs(index + 1 , sumW , sumC) ; // 未選擇第 index 件物品
    if(sumW + w[index] <= v ) { // 只有當選擇第 index 件物品之後,容量不會超過 v ,才進行選擇
        if(sumC + c[index] > maxValue)
            maxValue = sumC + c[index] ;  // 更新最大價值 maxValue
        dfs(index + 1 , sumW + w[index] , sumC + c[index]) ; // 選擇第 index 件物品
	}
}

2 最優方案

題目描述:

​ 給定一個序列,列舉這個序列的所有子序列(可以不連續)。例如對序列{1,2,3}來說,他的所有子序列為{1},{2},{3},{1,2},{1,3},{1,2,3}。列舉所有子序列的目的很明顯——可以從中選擇一個“最優”子序列,使它的某個特徵是所有子序列中最優的;如果有需要,還可以將這個最優子序列儲存下來。顯然,這個問題也可以等價於 列舉從 N 個整數中選擇 K 個數的所有方案。

​ 給定 N 個整數(可能有負數),從中選擇 K 個數,使得這 K 個數的和恰好等於給定的一個整數 X ; 如果有多種方案,則選擇他們當中平方和最大的一個。資料保證這樣的方案唯一。例如,從 4 個整數{2,3,3,4}中選擇兩個數,使他們的和為 6 。顯然有兩種方案{2,4} 和{3,3} ,其中平方和最大的方案為{2,4}。

輸入描述:

​ 第一行輸入 3 個正整數 ,依次輸入序列的長度 N,從中選擇數的數量 K ,和恰好等於的和 X

​ 第二行輸入 N 個整數(可能有負數),表示序列中的數。

輸出描述:

​ 輸出分兩行,第一行輸出 K 個數 , 表示最優序列中的元素。元素之間用空格分隔,且行末沒有於的空格

​ 第二行輸出 最優方案的平方和。

樣例輸入:

4 2 6
2 3 3 4

樣例輸出:

2 4
20
#include<bits/stdc++.h>
using namespace std ;
#define MAX 100010
int n , k , x , maxSumSqu = - 1 , a[MAX] ;//分別表示,序列長度,選擇數量,恰好的和,最大平方和,序列
vector<int> temp , ans ;  // temp 存放臨時方案,ans 存放平方和最大的方案 
// dfs引數列表分別表示 處理第 index 個數,現在已選 nowK 個數,當前已選數字的和 sum 及其平方和 sumSqu
void dfs(int index , int nowK , int sum , int sumSqu){
	// 找到第 k 個數的和為 x 
	if(nowK == k && sum == x){
		if(sumSqu > maxSumSqu){ // 如果比當前的更優 
			maxSumSqu = sumSqu ; // 更新最大平方和 
			ans = temp ; // 更新最優方案 
		}
		return ;
	}
	// 已經處理完 n 個數,或者超過 k 個數,或者和超過 x,返回 
	if(index == n || nowK > k || sum > x) return ; 
	// 選第 index 個數
	temp.push_back(a[index]) ;// 將第 index 個數 入棧,然候搜尋下一個數,注意引數各個值的變化
	dfs(index + 1 , nowK + 1 , sum + a[index] , sumSqu + a[index] * a[index]) ;
	// 不選第 index 個數
	temp.pop_back() ; // 先將第 index 個數 出棧( 還原現場 )  
	dfs(index + 1 , nowK , sum , sumSqu) ; // 繼續搜尋下一個數
}
int main(){
    //cin >> n >> k >> x ; 
	scanf("%d%d%d" , &n , &k , &x) ;// 依次輸入序列的長度 n , 選擇數的數量 k ,恰好的和 x
	for(int i = 0 ; i < n ; i ++) scanf("%d" , &a[i])  ; // 輸入序列
	dfs(0 , 0 , 0 , 0) ; // 呼叫 dfs 
	for(int i = 0 ; i < ans.size() ; i ++){ // dfs 後 , ans中儲存的是最優子序列,遍歷輸出
        //cout<< ans[i]<<" " ;
        printf("%d%c" , ans[i] , (i == ans.size() - 1)?'\n':' ') ;
	}
    printf("%d\n" , maxSumSqu) ; // 輸出最優子序列的 平方和 
	// cout<<endl<<maxSumSqu<<endl ; 
	return 0 ;
}

3 全排列

題目描述:

​ 排列與組合是常用的數學方法。先給一個正整數 ( 1 < = n < = 10 ) 。例如n=3,所有組合,並且按字典序輸出:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

輸入描述:

​ 在一行中輸入正整數 n ( 1 < = n < = 10 ) 。

輸出描述:

​ 按字典序輸出 n 的全排列。每行輸出一個排列,數字間用空格分隔,且行末沒有多與的空格

樣例輸入:

3

樣例輸出:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include<bits/stdc++.h>
using namespace std ;
#define MAX 20 
bool flag[20] = { false } ; // 是否被訪問,初值為 false
int num[20] ; // 儲存排列
int n ; 
// dfs 引數 index 表述處理排列的第 index 個數
void dfs(int index){
	if(index == n + 1){ // 如果 index 等於 n + 1 ,表明已經完成了一次排列 ,則當前排列
		 // printf("%d" , num[1]) ; 輸出第一個元素
		// for(int i = 2 ; i <= n ; i ++)printf(" %d" ,num[i]); 輸出空格和後面 n - 1 個元素
		// printf("\n") ; 當前排列全部輸出,換行
        // 上述四條語句,等價於如下語句
        for(int i = 1 ; i <= n ; i ++) printf("%d%c" , num[i] , (i == n)?'\n':' ');
		return ; 
	}
	for(int i = 1 ; i <= n ; i ++){ // n 的全排列,就是將 1 - n 都訪問一遍 , 並存在對應位置
		if(flag[i] == false){ // 如果 i 還未訪問 
			num[index] = i ; // 將 i 的值存入 num 陣列第 index 位置
			flag[i] = true ; // 將 i 的訪問標誌置為 true
			dfs(index + 1) ;  // 進行下一個位置的 dfs 搜尋
			flag[i] = false ; // 還原現場
		}
	}
}
int main(){
	scanf("%d" , &n) ; // 輸入正整數 n 
	dfs(1) ; // 呼叫 dfs
	return 0;
}

4 組合的輸出

題目描述:

​ 排列與組合是常用的數學方法,其中組合就是從 n 個元素中抽出 r 個元素(不分順序且 r ≤ n),我們可以簡單地將 n 個元素理解為自然數 1,2,…,n ,從中任取 r 個數。 例如 n = 5 ,r = 3 ,所有組合為:

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

輸入描述:

​ 在一行中輸入兩個自然數 nr ( 1 < n < 21,1 ≤ r ≤ n )

輸出描述:

​ 輸出所有的組合,每一個組合佔一行且其中的元素按由小到大的順序排列,所有的組合也按字典順序。每行中的元素之間用空格分隔,並且行末沒有多餘的空格。

樣例輸入:

5 3

樣例輸出:

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
#include<bits/stdc++.h>
using namespace std ;
int n , r ;
vector<int> temp ; // temp 儲存當前已選的數
// index 為當前處理的數的下標, nowR是已經有多少個數 
void dfs(int index , int nowR){
	if(nowR == r){ // 如果已選 k 個數,那麼輸出當前選擇的 k 個數
//		printf("%d" ,temp[0]);
//		for(int i = 1 ; i < r ; i ++) printf(" %d" , temp[i]);
//		printf("\n");
		for(int i = 0 ; i < r ; i ++) printf("%d%c" , temp[i] , (i == r - 1)?'\n':' ');
		return ;
	} 
    // 如果已經處理完 n 個數 或者選擇數的數量大於 k
	if(index == n + 1 || nowR > r) return ; 
	// 選第 index 個數
	temp.push_back(index) ; // 將 index 入棧 
	dfs(index + 1 , nowR + 1) ; // 繼續 dfs 深搜 注意引數的變化
	// 不選第 index 個數
	temp.pop_back() ; // 先將 index 出棧
	dfs(index + 1 , nowR) ;  // 繼續 dfs 深搜 注意引數的變化
}
int main(){
	scanf("%d%d" , &n, &r) ; 
	dfs(1 , 0) ; // 呼叫 dfs 下標解釋為 從 1 開始搜尋,當前選擇數的數量為 0 
	return 0 ;
}

5 組合+判斷素數

題目描述:

​ 已知 n 個整數b1,b2,…,bn。以及一個整數 k(k<n)。 從 n 個整數中任選 k 個整數相加,可分別得到一系列的和。例如當 n = 4,k=3,4 個整數分別為 3,7,12,19 時,可得全部的組合與它們的和為:

3+7+12=22  3+7+19=29  7+12+19=38  3+12+19=34。

現在,要求你計算出和為素數共有多少種。例如上例,只有一種的和為素數:3+7+19=29。

輸入描述:

  第一行兩個整數:`n` , `k`  `(1 ≤ n ≤ 20,k < n)` 

​ 第二行 n個整數:x1,x2,… , xn (1 ≤ xi ≤ 5000000)

輸出描述:

​ 一個整數(滿足條件的方案數)。

樣例輸入:

4 3
3 7 12 19

樣例輸出:

1
#include<bits/stdc++.h>
using namespace std ;
int num[22] ; // 儲存選擇的數
int n , k , ans = 0 ; // ans 用來儲存滿足要求的方案數的數量
// 判斷一個數是否為素數的演算法 就不必再贅述了
bool isPrime(int x){
	if(n <= 1 || n == 2) return false ; 
	for(int i = 2 ; i <= sqrt(x) ; i ++){
		if(x % i == 0)
			return false ; 
	}
	return true ;
}
// dfs 引數說明:分別表示 處理第 index 個數,當前已選 nowK 個數 ,當前已選數字的和
void dfs(int index , int nowK ,int sum){
	if(nowK == k){ // 如果已選 k 個數 
		if(isPrime(sum)) // 判斷當前 k 個數的和 是否為素數, 
			ans ++ ; // 如果是,則方案數 自增 1
		return ;
	}
    // 已經處理了 n 個數 , 或者 所選數的數量大於 k ,則返回
	if(index == n + 1 || nowK > k) return ; 
	// 選第 index 個數 ;注意 對應引數變化
	dfs(index + 1 , nowK + 1 , sum + num[index]) ;
	// 不選第 index 個數 ;
	dfs(index + 1 , nowK , sum) ; 
} 
int main(){
	scanf("%d%d" , &n ,&k) ; // 輸入 n 和 k
	for(int i = 1 ; i <= n ; i ++) scanf("%d" ,&num[i]) ; // 輸入 n 個整數
	dfs(1 , 0 , 0) ; // dfs 呼叫, 處理第 1 個數 當前所選數的數量為 0 當前所選數字的和為 0
	printf("%d\n" , ans) ; // 輸出滿足要去的結果數
	return 0;
}

6 N 皇后問題

題目描述:

​ 會下國際象棋的人都很清楚:皇后可以在橫、豎、斜線上不限步數地吃掉其他棋子。如何將 8 個皇后放在棋盤上(有 8 * 8 個方格),使它們誰也不能被吃掉!這就是著名的八皇后問題。

輸入描述:

​ 一個整數 n( 1 ≤ n ≤ 10 )

輸出描述:

​ 每行輸出對應一種方案,按字典序輸出所有方案。每種方案順序輸出皇后所在的列號,相鄰兩數之間用空格隔開,行末沒有多與的空格。如果一組可行方案都沒有,輸出 no solute!

樣例輸入:

4

樣例輸出:

2 4 1 3
3 1 4 2
#include<bits/stdc++.h>
using namespace std ;
int n , temp[12] = { 0 } ,cnt = 0 ;  // temp 用來儲存當前擺放皇后的列標 , cnt 用來記錄解的個數
bool p[12] = { false } ; // 列表的訪問陣列 , 初值為 false 代表都未被訪問
// dfs 引數 index 表示當前處理 第 index 列
void dfs(int index){
	if(index == n + 1){ // 已經處理完 n 列 , 輸出當前 temp 中儲存的擺放順序
		cnt ++ ; 
		//printf("%d" ,temp[1]);
		//for(int i = 2 ; i <= n ; i ++) printf(" %d" , temp[i]);
		//printf("\n");
        for(int i = 1 ; i <= n ; i ++) printf("%d%c" , temp[i] , (i == n)?'\n':' ') ;
		return ;
	}
    // 對於 擺放皇后的位置,遍歷第 i 到 n 列
	for(int i = 1 ; i <= n ; i ++){
		if(p[i] == false){ // 如果當前列沒有皇后
			bool flag = true ; // 定義標誌位,看是否對角線上有皇后
			for(int pre = 1 ; pre < index ; pre ++){ // 遍歷已經擺放好的皇后
				// 回溯 
				if(abs(index - pre) == abs(i - temp[pre])){ // 如果在某一對角線有衝突
					flag = false ; // 標誌位置為 false ,並跳出迴圈
					break; 
				}
			}
			if(flag){ // 如果對角線沒有衝突
				temp[index] = i ; // 將當前列標複製給 temp 的第 index ,即在當前列放一個皇后
				p[i] = true ; // 並置當前位置的訪問位 為true 表示已經訪問過
				dfs(index + 1) ; // 繼續 dfs ,擺放下一個位置的皇后
				p[i] = false ; // 還原現場
			}
		}
	}
}
int main(){
	scanf("%d" , &n) ; // 輸入 n 皇后 的規模 n
	dfs(1) ;  // 從 第 1 列開始 dfs
	if(cnt == 0) printf("no solute!\n") ; // 如果解的數目為 0 即無解,按題目要求輸出 no solute!
	return 0;
}

7 出棧序列統計

題目描述:

​ 棧是常用的一種資料結構,有 n 個元素在棧頂端一側等待進棧,棧頂端另一側是出棧序列。你已經知道棧的操作有兩種:push pop ,前者是將一個元素進棧,後者是將棧頂元素彈出。現在要使用這兩種操作,由一個操作序列可以得到一系列的輸出序列。請你程式設計求出對於給定的n,計算並輸出由運算元序列 1,2,… ,n,經過一系列操作可能得到的輸出序列總數。

輸入描述:

​ 一個整數 n (1 ≤ n ≤ 15).

輸出描述:

​ 一個整數,即可能輸出序列的總數目。

樣例輸入:

3

樣例輸出:

5
#include<bits/stdc++.h>
using namespace std ;
int n , cnt = 0 ;
// dfs 引數解析,in 代表棧中有多少個元素 , out 代表還有多少元素需要入棧
void dfs(int in , int out){
	if(out == 0){ // 如果以及沒有需要進棧的元素,那麼出棧順序總數目 + 1
		cnt ++ ; 
		return ;	
	}
	if(in == 0) dfs(in + 1,out - 1) ; // 如果棧中沒有元素,則只能進行入棧操作
	else if (out != 0){ // 如果棧中有元素,也存在待入棧的元素
		dfs(in + 1 , out - 1 ) ;	// 進行入棧操作
		dfs(in - 1 , out) ;	// 進行出棧操作
	}
}
int main(){
	scanf("%d" , &n) ; 
	dfs(0 , n) ; // 呼叫 dfs 初始棧內元素個數為 0 ,需要入棧元素為 n
	printf("%d\n" , cnt) ; // 輸出解的個數
	return 0 ;
}

8 走迷宮

題目描述: 

  有一個n * m格的迷宮(表示有 n 行、m 列),其中有可走的也有不可走的,如果用 1 表示可以走,0 表示不可以走,檔案讀入這 n * m個資料和起始點、結束點(起始點和結束點都是用兩個資料來描述的,分別表示這個點的行號和列號)。現在要你程式設計找出所有可行的道路,要求所走的路中沒有重複的點,走時只能是上下左右四個方向。如果一條路都不可行,則輸出相應資訊(用-l表示無路)。 請統一用 左上右下的順序擴充,也就是 (0,-1),(-1,0),(0,1),(1,0)

輸入描述:

​ 第一行是兩個數 n,m ( 1 < n , m < 15 ),接下來是m行n列由1和0組成的資料,最後兩行是起始點和結束點。

輸出描述:

​ 所有可行的路徑,描述一個點時用(x,y)的形式,除開始點外,其他的都要用->表示方向。 如果沒有一條可行的路則輸出 -1

樣例輸入:

5 6
1 0 0 1 0 1
1 1 1 1 1 1
0 0 1 1 1 0
1 1 1 1 1 0
1 1 1 0 1 1
1 1
5 6

樣例輸出:

(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)

提示資訊:

用一個a陣列來存放迷宮可走的情況,另外用一個陣列b來存放哪些點走過了。每個點用兩個數字來描述,一個表示行號,另一個表示列號。對於某一個點(x,y),四個可能走的方向的點描述如下表:
  2
1 x,y  3
  4
 對應的位置為:(x, y-1),(x-1, y),(x, y+1),(x+1, y)。所以每個點都要試探四個方向,如果沒有走過(陣列b相應的點的值為0)且可以走(陣列a相應點的值為1)同時不越界,就走過去,再看有沒有到達終點,到了終點則輸出所走的路,否則繼續走下去。
 這個查詢過程用search來描述如下:
procedure search(x, y, b, p);{x,y表示某一個點,b是已經過的點的情況,p是已走過的路}
begin
  for i:=1 to 4 do{分別對4個點進行試探}
  begin
    先記住當前點的位置,已走過的情況和走過的路;
    如果第i個點(xi,yi)可以走,則走過去;
    如果已達終點,則輸出所走的路徑並置有路可走的資訊,
    否則繼續從新的點往下查詢search(xi,yi,b1,p1);
  end;
end;
 有些情況很明顯是無解的,如從起點到終點的矩形中有一行或一列都是為0的,明顯道路不通,對於這種情況要很快地“剪掉”多餘分枝得出結論,這就是搜尋裡所說的“剪枝”。從起點開始往下的一層層的結點,看起來如同樹枝一樣,對於其中的“枯枝”——明顯無用的節點可以先行“剪掉”,從而提高搜尋速度。

#include<bits/stdc++.h>
using namespace std ;
#define MAX 20
// 下列變數代表迷宮的維數, 解的數量,起始座標和出口座標  
int n , m , num = 0 , startX , startY , endX , endY ;
int labyrinth[MAX][MAX] = { 0 } ; // 儲存迷宮的二維陣列 
int dx[4] = {0 , -1 , 0 , 1 } ; 
int dy[4] = {-1 , 0 , 1 , 0 } ; // 表示上下左右的擴充套件
// 代表所有是解的路徑上的點的數量(除終點) 
vector<pair<int , int > > road ; //儲存路線的向量,每個元素的有x、y座標 
// x ,y 代表當前處理的座標點,
void dfs(int x , int y ){
	road.push_back(make_pair(x , y )) ; // 當前座標的賦值	
	if(x == endX && y == endY){ // 如果已經走到出口位置,輸出這條路徑
		for(int i = 0 ; i < road.size() ; i ++){ 
			printf("(%d,%d)" , road[i].first , road[i].second) ; 
			if(i != road.size() - 1) printf("->") ;
		}
		printf("\n") ; 
		num ++ ;// 解的數量加 1 
		return ;
	}
	// 遍歷當前座標的左,上,右,下位置的點
	for(int j = 0 ; j < 4 ; j ++){
        // 如果進行 左,上,右,下 擴充套件後,得到的點依舊未被訪問且 在迷宮的範圍內
		if(labyrinth[x + dx[j]][y + dy[j]] == 1 && 1 <= x + dx[j] <= m && 1 <= y + dy[j] <= n){
			labyrinth[x][y] = 0 ; //當前點以遍歷,不可訪問
			dfs(x + dx[j] , y + dy[j]) ; // 選擇某個位置(左,上,右,下),繼續遍歷搜尋
			road.pop_back() ; // 當前元素出棧
			labyrinth[x][y] = 1 ; // 還原現場 
		}
	} 
}
int main(){
	scanf("%d%d" , &m , &n) ;// 輸入迷宮的行與列的值 
	for(int i = 1 ; i <= m ; i ++){
		for(int j = 1 ; j <= n ; j ++){
			scanf("%d" , &labyrinth[i][j]) ; // 輸入迷宮
		}
	}
	scanf("%d%d" , &startX , &startY) ; // 輸入起始點座標
	scanf("%d%d" , &endX , &endY) ;// 輸入終點座標
	dfs(startX , startY) ; // 從輸入的起點開始 dfs
	if(num == 0) printf("-1\n") ; // 如果找不到路徑,則輸出 -1 
	return 0 ;
}