[回溯法] 1 求n個元素的集合的冪集
阿新 • • 發佈:2018-11-12
問題
求含n個元素的集合的冪集
【註釋】冪集:所有子集所組成的集合
【舉例】
A={1,2,3}
ρ(A) = { {1,2,3}, {1,2}, {1,3}, {1}, {2,3}, {2}, {3}, ∅ }
思路
本問題可以用【分治法】來求解
從另一個角度分析問題:
冪集的每個元素時一個集合,它或是空集,或含集合A中的一個元素,或含集合A中兩個元素,或等於集合A
【△】反之,從集合A的每個元素來看,它只有兩個狀態:
- 屬於冪集的元素集
- 不屬於冪集元素集
【結論】ρ(A)的元素的過程可看成是依次對集合A中元素進行“取”或“舍(棄)”的過程
做法
冪集元素在生成過程中的狀態圖:
可以用一棵二叉樹,來表示過程中冪集元素的狀態變化狀況
- 【樹中的根結點】冪集元素的初始狀態,為空集
- 【葉子結點】它的終結狀態
- 【第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); //不選中的,下一步 } }