[數字技巧]子集問題(尋找給定集合的所有子集)


  我們定義該問題如下:

  給定一個集合C,找出所有的集合C',使得C'包含於C。

一、無重復元素的集合

  我們首先來考慮一種簡單的情形,C中的數都是各不相同的,這就意味着所產生的子集不會有重復的。

  直觀來說,求一個集合的子集,無非就是對每個元素進行枚舉,枚舉兩種狀態”選“還是”不選“。例如,對一個集合C,當對cur這個位置的元素進行枚舉時,對剩余的元素可以遞歸調用這個枚舉的過程,當cur為數組長度n時,代表枚舉結束。

  子集的解空間又叫子集樹,其葉子節點所代表的狀態即為所有的子集。例如,集合{1,3,5},其子集樹如下所示:

  如上圖所示,共有8個葉子節點,代表8個子集,有了子集樹,要求出所有的子集,顯然就是一個二叉樹的先序遍歷問題了,這里我們要設置一個與原數組一樣大的標志數組,來標志當前位置”選“還是”不選“,因此,這種方法又叫”位向量法“,代碼如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int n;
 5 int A[20];
 6 int B[20];
 7 
 8 void print_subset(int n, int* B, int cur)
 9 {
10     if(cur == n)
11     {
12         for(int i = 0; i < cur; i++)
13             if(B[i]) printf("%d ", A[i]);        // 打印當前集合
14         printf("\n");
15         return;
16     }
17     B[cur] = 1;                                // 選第 cur 個元素
18     print_subset(n, B, cur+1);
19     B[cur] = 0;                                // 不選第 cur 個元素
20     print_subset(n, B, cur+1);
21 }
22 
23 int main()
24 {
25     scanf("%d",&n);
26     for(int i=0; i<n; i++)
27         scanf("%d",&A[i]);
28 
29     print_subset(n,B,0);
30     system("Pause");
31     
32     return 0;    
33 }
View Code

  另一種有趣的構造子集的方法叫”增量構造法“,顧名思義,這種做法是一種增量式的做法,例如,對集合{1,3,5},首先確定第一個元素為1,這就是一個子集,然后再這個基礎上決定是不是增加3,或者5,形成另外兩個子集{1,3},{1,5},{1,3}再可以決定是不是要繼續增加5,而{1,5}中5已經到了最后,因此不能再添加,同樣,可以確定第一個元素為2,重復上述步驟。注意,這里集合是定序的,以防止重復。下圖給出了使用”增量構造法“對{1,3,5}進行子集搜索的子集樹。

  

  下面我們給出使用”增量構造法“的子集生成代碼:

 1 //增量構造法
 2 
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 
 6 int n;
 7 int A[20];
 8 int B[20];
 9 
10 void print_subset(int n, int* A, int cur);
11 
12 int main()
13 {
14     scanf("%d",&n);
15     for(int i=0; i<n; i++)
16         scanf("%d",&A[i]);
17 
18     print_subset(n,B,0);
19     system("Pause");
20     
21     return 0;    
22 }
23 
24 void print_subset(int n, int* B, int cur)
25 {
26     int i;
27     for(i= 0; i < cur; i++)    printf("%d ",A[B[i]]);
28     printf("\n");
29     int s = cur ? B[cur-1]+1 : 0;//確定當前元素的最小可能位置 
30     for(i = s; i < n; i++)
31     {
32         B[cur] = i;
33         print_subset(n, B, cur+1);//遞歸構造子集 
34     }
35 }
View Code

  增量構造法可以很容易構造出指定大小的子集,只需要控制cur的大小指定輸出即可,比如要輸出長度不超過的3的子集,只要在程序一開始加上 if(cur>3) return; ,要輸出長度為3的,只需要在for循環外加一個if就行了。

練習題:

  1、http://www.cnblogs.com/codershell/p/3619928.html

二、含有重復元素的集合

  如果原數組中存在重復的元素,那么用上述方法在子集生成時就會產生重復的子集,這里我是采用最簡單的查重方法。代碼如下:

1 bool isExist(vector<vector<int> > &vv,vector<int> v){
2         for(int i=0; i<vv.size(); i++)
3             if(vv[i] == v)
4                 return true;
5                 
6         return false;
7 }
View Code

  不知道有沒有什么巧妙的方法來解決這個問題。

練習題:

  http://www.cnblogs.com/codershell/p/3621687.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM