Java 迷宮問題,複雜多條件迷宮問題

回溯法是一種不斷試探且及時糾正錯誤的搜索方法,下面的求解過程採用回溯法。從入口出發,按某一方向向前探索,若能走通(未走過的),即某處可以到達,則到達一個新點,否則試探下一個方向;若所有的方向均沒有通路,則沿原路返回前一點,換下一個方向繼續試探,直到所有可能的通路都搜索到,或找到一條通路,或無路可走又返回到入口點。這裏可以用一個棧來實現,每走一步,將該位置壓入棧中,若該點無路可走,則出棧返回上一位置。

總結與思路:

一般的簡單迷宮問題,類似於能否出去。可以使用“染色”的思路,一個節點被染紅之後傳給相鄰的節點,最後如果出口節點也被染紅,那麼可以出去。從代碼實現上看,

  1. 傳入當前節點的信息,每次方法開始,判斷是否爲出口。是就可以提前退出,不是則繼續向下執行。
  2. 建立迷宮大小的靜態boolean數組,標識每個節點是否已被染色,避免死循環。
  3. 將本節點染色,判斷是否有上下左右節點,有且未被染色,則調用本方法並傳入將該節點信息(即遞歸調用)。
  4. 方法調用完畢,判斷出口是否已經被染色。

複雜迷宮問題,查找最短路徑等。

  1. 基本步驟同上,需注意幾個問題。
  2. 考察最短路徑,不同路徑之間可以有一段重複。不應該再使用全局的Boolean數組。可以考慮使用List.contains()方法避免有重複的點。
  3. 需要保存路徑時,使用回溯法。一般應使用stack,本題使用的是list,在執行下一次方法前將本節點加入list。然後一直向前探索,如果到了出口則保存下來,沒到就不保存。遞歸方法返回時應該將本節點移除list,因爲如果沒到出口說明此路不通,無需保存本節點,如果到了出口,則已經加到result中了,不需要在保存本節點了。都應當移除,再從上一級節點探索。


遇到滴滴筆試題,迷宮上加了些其他因素

import java.util.*;

/**
 * @author XF 迷宮求路徑,類似“染色”
 */
public class Main {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		// 輸入參數 測試:4 4 10 1 0 0 1 1 1 0 1 0 1 1 1 0 0 1 1
		int n = sc.nextInt();
		int m = sc.nextInt();
		int p = sc.nextInt();
		int[][] mi = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				mi[i][j] = sc.nextInt();
			}
		}
		flag = new boolean[n][m];
		List result = new ArrayList<>();
		List tempList = new ArrayList<>();
		
		// 調用計算方法,傳入參數。獲得result.
		cc(0, 0, mi, result, tempList);

		// 比較消耗精力最少的
		int shortestNum = 0;
		List temp = (List) result.get(0);
		int min = (int) temp.get(temp.size() - 1);
		if (result.size() > 1) {
			for (int i = 1; i < result.size(); i++) {
				temp = (List) result.get(i);
				int tempMin = (int) temp.get(temp.size() - 1);
				if (tempMin < min) {
					min = tempMin;
					shortestNum = i;
				}
			}
		}
		List r = (List) result.get(shortestNum);
		r.remove(r.size() - 1);
		for(int i=0; i<r.size() ;i++){
			System.out.print(r.get(i)+",");
		}
		System.out.println("[0,3]");
		
	}

	// 輔助計算的靜態變量
	static boolean[][] flag; // 爲數組每個元素設置標誌,避免重複到達某點造成多次計算或死循環
	static int pu; // pUsed 每個成功到達出口的序列計算消耗的體力值

	/*
	 * 計算迷宮路徑方法,主要使用遞歸調用每個節點的上下左右。上下左右節點又可以調用它的上下左右。 按消耗體力的數量及出口位置,優先順序爲上、右、下、左
	 * 傳入參數,形參,即引用變量,地址不變,但指向的地址的值可以變化.
	 */
	public static void cc(int i, int j, int[][] mi, List result, List tempList) {
		// flag[i][j] = true;
		// //本題是考察最短路徑,不同路徑之間可以有一段重複。應使用list.contains()判斷是否已包含此節點。
		// 不管從哪條路到了出口,新建list存入結果,並保存pu
		if (i == 0 && j == mi[0].length - 1) {
			tempList.add(pu);
			result.add(new ArrayList<>(tempList));
			tempList.remove((Integer) pu);
		}
		/*
		 * up,從當前節點,考慮向上。如果上個節點有且沒有使用,將上個節點作爲參數傳入方法。其他同理。
		 * 將當前節點信息(x,y)存入list,調用完方法移除!!!非常重要!!核心!!
		 * 因爲不管是否到達出口,都說明已經探索完從這個節點伸出的路徑了,應當移除,則可以從此節點上個節點繼續探索。
		 * 到達了出口,已經保存了,該移除;沒到,此路不通,移除。 根據需求應當使用stack,我使用不多就用list代替了。
		 */
		if (i > 0 && mi[i - 1][j] == 1
				&& !tempList.contains("[" + (i - 1) + "," + j + "]")) {
			tempList.add("[" + i + "," + j + "]");
			pu += 3;
			cc(i - 1, j, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
			pu -= 3;
		}
		// right
		if (j < mi[0].length - 1 && mi[i][j + 1] == 1
				&& !tempList.contains("[" + i + "," + (j + 1) + "]")) {
			tempList.add("[" + i + "," + j + "]");
			pu += 1;
			cc(i, j + 1, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
			pu -= 1;
		}
		// down
		if (i < mi.length - 1 && mi[i + 1][j] == 1
				&& !tempList.contains("[" + (i + 1) + "," + j + "]")) {
			tempList.add("[" + i + "," + j + "]");
			cc(i + 1, j, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
		}
		// left
		if (j > 0 && mi[i][j - 1] == 1
				&& !tempList.contains("[" + i + "," + (j - 1) + "]")) {
			tempList.add("[" + i + "," + j + "]");
			pu += 1;
			cc(i, j - 1, mi, result, tempList);
			tempList.remove("[" + i + "," + j + "]");
			pu -= 1;
		}
	}
}
下面幾個網上的迷宮問題傳送門:

 染色

stack 解決迷宮問題

A*算法 Java代碼實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章