1. 程式人生 > >[回溯法] 1 求n個元素的集合的冪集

[回溯法] 1 求n個元素的集合的冪集

問題

求含n個元素的集合的冪集

【註釋】冪集:所有子集所組成的集合
【舉例】

A={1,2,3}
ρ(A) = { {1,2,3}, {1,2}, {1,3}, {1}, {2,3}, {2}, {3}, ∅ }

思路

本問題可以用【分治法】來求解

從另一個角度分析問題:
冪集的每個元素時一個集合,它或是空集,或含集合A中的一個元素,或含集合A中兩個元素,或等於集合A
【△】反之,從集合A的每個元素來看,它只有兩個狀態:

  1. 屬於冪集的元素集
  2. 不屬於冪集元素集

【結論】ρ(A)的元素的過程可看成是依次對集合A中元素進行“取”或“舍(棄)”的過程

做法

冪集元素在生成過程中的狀態圖:
冪集元素在生成過程中的狀態圖

可以用一棵二叉樹,來表示過程中冪集元素的狀態變化狀況

  1. 【樹中的根結點】冪集元素的初始狀態,為空集
  2. 【葉子結點】它的終結狀態
  3. 【第i層的分支結點】已對集合A中前i-1個元素進行了取/舍處理的當前狀態

【結論】求冪集元素的過程即為先序遍歷這棵狀態樹的過程

void PowerSet(int i,int n) {
	// 求含n個元素的集合A的冪集ρ(A)。進入函式時已對A中前i-1個元素坐了取捨處理
	// 現從第i個元素起進行取捨處理。若i>n,則求得冪集的一個元素,並輸出之
	// 初始呼叫:PowerSet(1,n);
	if (i>n) 輸出冪集的一個元素
	else {
		取第i個元素;PowerSet(i+1, n);
		舍第i個元素;PowerSet(i+1, n);
	}
}

拓展

圖中狀態變化樹是一棵滿二叉樹:樹中每個葉子結點的狀態都是求解過程中可能出現的狀態(即問題的解)。
【然而】很多問題用回溯和試探求解時,描述求解過程的狀態樹不是一棵滿的多叉樹

【非滿多叉樹】不是滿的多叉樹:當試探過程中出現的狀態和問題所求解產生矛盾時,不再繼續試探下去,這時出現的葉子結點不是問題的解的終結狀態
此類問題的求解過程可看成是在約束條件下進行先序(根)遍歷,並在遍歷過程中剪去那些不滿足條件的分支
【例子】四皇后問題

程式碼

void GetPowerSet(int i, List A, List &B) {
	// 線性表A表示集合A,線性表B表示冪集ρ(A)的一個元素
	// 區域性量k為進入函式時表B的當前長度
	// 第一次呼叫本函式時,B為空表,i=1
	if (i>ListLength(A)) Output(B); //輸出當前B值,即ρ(A)的一個元素
	else {
		GetElem(A, i, x); //得到A的第i個元素
		k = ListLength(B);
		ListInsert(B, k+1, x); //選中
		GetPowerSet(i+1, A, B); //選中的,下一步
		ListDelete(B, k+1, x); //不選中
		GetPowerSet(i+1, A, B); //不選中的,下一步
	}
}