消消樂實驗總結與反思

Xindolia_Ring發表於2018-11-07

問題重述

背景介紹:
《開心消消樂》是一款樂元素研發的三消類休閒遊戲。遊戲中消除的物件為小動物的頭像,包括小浣熊、小狐狸、小青蛙和小雞等動物頭像。玩家通過移動動物頭像位置湊夠同行/同列3個或3個以上即可消除。

現在定義消消樂規則如下:
1.遊戲中共允許K種物件,分佈在大小為M×N的格子布局中。
2.交換兩個物件位置,湊夠3個或3個以上即可消除,被消除的空格由正上方物件掉落填充。
3.可能出現三種消除方式: 同行(列)三個、同行(列)四個、同行(列)五個。分別 得分1分、4分、10分。
4.當沒有可通過交換消除的物件時,遊戲終止。

回溯法 [ Back Tracking Method ]
(探索與回溯法)是一種選優搜尋法,又稱為試探法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為“回溯點”。(取自百度百科)

回溯法解題的關鍵要素
確定了問題的解空間結構後,回溯法將從開始結點(根結點)出發,以深度優先的方式搜尋整個解空間。開始結點成為活結點,同時也成為擴充套件結點。在當前的擴充套件結點處,向縱深方向搜尋並移至一個新結點,這個新結點就成為一個新的活結點,併成為當前的擴充套件結點。如果在當前的擴充套件結點處不能再向縱深方向移動,則當前的擴充套件結點就成為死結點。此時應往回移動(回溯)至最近的一個活結點處,並使其成為當前的擴充套件結點。回溯法以上述工作方式遞迴地在解空間中搜尋,直至找到所要求的解或解空間中已無活結點時為止。

運用回溯法解題的關鍵要素有以下三點:
(1) 針對給定的問題,定義問題的解空間
(2) 確定易於搜尋的解空間結構;
(3) 以深度優先方式搜尋解空間,並且在搜尋過程中用剪枝函式避免無效搜尋。

遞迴和迭代回溯
一般情況下可以用遞迴函式實現回溯法,遞迴函式模板如下:

void BackTrace(int t) {
	if(t>n)
		Output(x);
	else
		for(int i = f (n, t); i <= g (n, t); i++ ) {
			x[t] = h(i);
			if(Constraint(t) && Bound (t))
				BackTrace(t+1);
		}
}

其中,t 表示遞迴深度,即當前擴充套件結點在解空間樹中的深度;n 用來控制遞迴深度,即解空間樹的高度。當 t>n時,演算法已搜尋到一個葉子結點,此時由函式Output(x)對得到的可行解x進行記錄或輸出處理。用 f(n, t)和 g(n, t)分別表示在當前擴充套件結點處未搜尋過的子樹的起始編號和終止編號;h(i)表示在當前擴充套件結點處x[t] 的第i個可選值;函式 Constraint(t)和 Bound(t)分別表示當前擴充套件結點處的約束函式和限界函式。若函式 Constraint(t)的返回值為真,則表示當前擴充套件結點處x[1:t] 的取值滿足問題的約束條件;否則不滿足問題的約束條件。若函式Bound(t)的返回值為真,則表示在當前擴充套件結點處x[1:t] 的取值尚未使目標函式越界,還需由BackTrace(t+1)對其相應的子樹做進一步地搜尋;否則,在當前擴充套件結點處x[1:t]的取值已使目標函式越界,可剪去相應的子樹。

採用迭代的方式也可實現回溯演算法,迭代回溯演算法的模板如下:

void IterativeBackTrace(void) {
	int t = 1;
	while(t>0) {
		if(f(n, t) <= g( n, t))
			for(int i = f(n, t); i <= g(n, t); i++ ) {
				x[t] = h(i);
				if(Constraint(t) && Bound(t)) {
					if ( Solution(t))
						Output(x);
					else
						t++;
				}
			}
		else 
			t− −;
	}
}

在上述迭代演算法中,用Solution(t)判斷在當前擴充套件結點處是否已得到問題的一個可行解,若其返回值為真,則表示在當前擴充套件結點處x[1:t] 是問題的一個可行解;否則表示在當前擴充套件結點處x[1:t]只是問題的一個部分解,還需要向縱深方向繼續搜尋。

用回溯法解題的一個顯著特徵是問題的解空間是在搜尋過程中動態生成的,在任何時刻演算法只儲存從根結點到當前擴充套件結點的路徑。如果在解空間樹中,從根結點到葉子結點的最長路徑長度為 h(n),則回溯法所需的計算空間複雜度為 O(h(n)),而顯式地儲存整個解空間複雜度則需要O(2h(n))或O(h(n)!)。

資料結構
個人推薦map,簡單點陣列就行,用堆疊也是OK的。

演算法步驟

  1. 每次遞迴都找出那個時候矩陣的所有可交換點,進行每個點的深度搜尋
  2. 遞迴結束條件有兩個
    ①沒有可交換點,回溯;
    ②剪枝,檢視map是否有相同狀態但分數更高的,如果有更高的,結束遞迴進行回溯;
    否則,替換掉分數低的進行下一步遞迴;
  3. 判斷頂點周圍是否存在可以消去的條件;可以通過判斷整行或者整列可否消去;
  4. 每次消去之後正上方的方塊會掉下來;
  5. 掉下來之後還要多一次判斷是否可以自動再消,直到確認不能再消。即每次消除+掉落為一個過程,完成一次之後就需要判斷。沒有->繼續搜尋判斷;有->繼續消去+下移;
  6. 進入下一次遞迴之前要把當前狀態記錄到map上。

相關文章