问题描述:分割集合成多个子集合,这几个子集合间没有交集且他们的并集是原集合。
思路:将包含n个元素的集合set的分割表示为n个数字。比如set[]={1,2,3,4},那么{1,2},{3,4}就可以表示为1122,这4个数分别表示set[0]在第一个分割集合,set[1]在第一个分割集合,set[2]在第二个分割集合,set[3]在第二个分割集合。将这个过程称为编码。
然后抓住两个要点:
1.第i个元素的编码一定小于或者等于i。约定一下,set原集合已经从小到大排列好,分割的集合也是这样排好。然后,很容易理解,第1个元素的编码肯定为1。接着,第2个元素如果在第一个分割集合中,那么他的编码也是1;但如果不是在第一个分割集合中,那么第2个元素的编码肯定是2,因为在第二个分割集合中,最小的数最少也是2;依次类推,可以很容易用数学归纳法证明。
2.第i个元素的编码一定小于等于1,2,3,....i-1这些元素的编码中的最大值再加1。这也很容易想到,也很容易用数学归纳法证明。
抓住这个要点后,就用深度搜索构造一颗子集合树就可以解决问题了。但是,我在构造子集树的过程中出现了一个思维上的错误,导致浪费了不少调试的时间。具体的错误见代码注释的描述。
代码如下:
1 #include <stdio.h> 2 #define MAX 1000 3 4 int set[MAX]={1,2,3,4}; 5 int n=4; 6 int code[MAX]={1,1,1,1}; 7 8 //prototype 9 void DFS(int index); 10 11 void print_set() 12 { 13 int index; 14 int max=-1; 15 printf("{"); 16 for(index=0;index<n;index++) 17 { 18 if(code[index]==1) 19 printf("%d ",set[index]); 20 if(max<code[index]) 21 max=code[index]; 22 } 23 printf("},{"); 24 for(index=2;index<=max;index++) 25 { 26 int index_2; 27 for(index_2=0;index_2<n;index_2++) 28 if(code[index_2]==index) 29 printf("%d ",set[index_2]); 30 printf("},{"); 31 } 32 printf("\n"); 33 } 34 35 //find out the max code of 1,2,3...i-1 and plus 1 36 int max_pre(int i) 37 { 38 int index; 39 int max=-1; 40 for(index=0;index<i;index++) 41 if(code[index]>max) 42 max=code[index]; 43 return (max+1); 44 } 45 46 int main() 47 { 48 int i; 49 DFS(1); 50 return 0; 51 } 52 53 void DFS(int index) 54 { 55 print_set(); 56 if(index==n) 57 return ; 58 for(;index<n;index++) 59 { 60 int temp=code[index]+1; 61 //在写这里的代码时,我出现了一个思维上的误区:在回溯时我只顾把code[index]++,而没有将code[index]在2到max_pre(index)遍历,这导致了我调试了挺长时间。 62 for(;temp<=index+1 && temp<=max_pre(index);temp++) 63 { 64 int save=code[index]; 65 code[index]=temp; 66 DFS(index+1); 67 code[index]=save; 68 } 69 70 } 71 72 }
参考资料:《C语言名题精选百则技巧篇》
如果你觉得我的文章对你有帮助,请推荐一下,非常感谢!