1.組合問題:
問題描述:對於一組各不相同的數字,從中任意抽取1-n個數字,構成一個新的集合。求出所有的可能的集合。例如,對於集合{1,2,3},其所有子集為{1},{2},{3},{1,2},{1,3},{2,3}{1,2,3}, 給定一個數組(元素各不相同),求出數組的元素的所有非空組合(即數組的所有非空子集)
解法一:位向量法。用一個輔助數組表示各個元素的狀態。1表示在集合中,0表示不在數組中。遞迴地求解所有的子集。
演算法描述如下://這裡的演算法對空集也輸出了,可修改之使得只輸出非空集合。
void getSubSet(int *a,int *b,int n,int k){ if(k==n){ for(int i = 0;i < n;i++){ if(i == 0){ printf("{ "); }else if(i==(n-1)){ printf(" }\n"); } if(b[i]){ printf("%d, ",a[i]); } } return ; } b[k] = 1; getSubSet(a,b,n,k+1); b[k] = 0; getSubSet(a,b,n,k+1); }
解法二:位元影像的思想。思路類似與解法一位向量。用n個位來儲存相應的元素是否在集合中,如果在集合中,相應位為1.否則為0;
程式碼範例://註:這裡用的是位元組而不是c++中的bitmap
void print_subset(int n,int s){ printf("{"); for(int i = 0;i<n;i++){ if(s&(1<<i)) printf("%d ",i);//或者a[i] } printf("}\n"); } void subset(int n){ for(int i= 0;i<(1<<n);i++){ print_subset(n,i); } }
只需要調用subset(n)即可輸出1->n個數位所有組合。或者修改輸出部分為輸出一個特定集合的組合。
2.。排列問題。
給定一組不相同的數字。求出這n個數位各種排列形式。稱為排列問題。
解法一:暴力搜尋,對於一個全排列問題,相當於搜尋一個具有n個n-1叉數的深林。暴力搜尋之,得到所有的全排列形式。代碼如下:
#include <stdio.h>#include <stdlib.h>void output(int *a,int n){for(int i = 0;i<n;i++){printf("%d ",a[i]);}printf("\n");}void perm(int *a,int *b,int n,int k){int i,j;if(n==k){output(b,n);}else{for( i = 0;i < n;i++){int flag = 1;for( j = 0;j < k;j++){if(b[j] == a[i]){flag = 0;}}if(flag){b[k] = a[i];perm(a,b,n,k+1);}}}}int main(){int *a =new int[3];int *b =new int[3];for(int i = 0;i<3;i++){a[i] = i+1;b[i] = i+1;}perm(a,b,3,0);}
解法二:類比回溯法產生排列的過程,對於已知的一個序列,如果交換其中兩個元素的,會得到新的序列。思路類似於產生組合問題。演算法描述如下:
void permutation(int *a, int n,int k){ if(n==k){ printf("{"); for(int i = 0;i<n;i++){ printf("%d ",a[i]); } printf("}\n"); return ; } for(int i = k;i<n;i++){ swap(&a[i],&a[k]); permutation(a,n,k+1); swap(&a[i],&a[k]); } }
解法三:c++ 中STL中next_permutation()方法。注意這種方法要得到所有的排列,需要原始數組為遞增有序的,可先對其qsort()
do{ printf("{"); for(int i = 0;i<N;i++){ printf("%d ",a[i]); } printf("}\n"); }while(next_permutation(a,a+N));
//TODO 全排列中有重複元素的演算法總結
3.笛卡爾積問題。
@xuzuning
問題描述:笛卡爾(Descartes)乘積又叫直積。設A、B是任意兩個集合,在集合A中任意取一個元素x,在集合B中任意取一個元素y,組成一個有序對(x,y),
把這樣的有序對作為新的元素,他們的全體組成的集合稱為集合A和集合B的直積,記為A×B,即A×B={(x,y)|x∈A且y∈B}。n對集合的笛卡爾積由此遞迴定義得到。
<?php /* * 笛卡爾(Descartes)乘積又叫直積。設A、B是任意兩個集合,在集合A中任意取一個元素x,在集合B中任意取一個元素y,組成一個有序對(x,y), * 把這樣的有序對作為新的元素,他們的全體組成的集合稱為集合A和集合B的直積,記為A×B,即A×B={(x,y)|x∈A且y∈B}。 * @author xuzuning */ function Descartes() { $t = func_get_args(); if(func_num_args() == 1) { return call_user_func_array( __FUNCTION__, $t[0] ); } $a = array_shift($t); if(! is_array($a)) { $a = array($a); } $a = array_chunk($a, 1);//目的是分解成array(a)這樣的形式,便於後面的合并 do { $r = array(); $b = array_shift($t); if(! is_array($b)) $b = array($b); foreach($a as $p) foreach(array_chunk($b, 1) as $q) $r[] = array_merge($p, $q); $a = $r; }while($t); return $r; } $arr = array( array('a1','a2',), 'b', array('c1','c2',), array('d1','d2','d3') ); $r = Descartes( $arr ); ?>
上述求全排列和子集的方法,稱為回溯法。對於回溯法,有一個基本的架構(模式):
void backTrack(int n,int k){ if(符合條件){ 輸出; return; } else{ 執行代碼; backTrack(n,k+1); 代碼回溯; } }
利用這個模式可以寫出例如組合,全排列,子集,八皇后等問題。
八皇后問題的解法:http://blog.csdn.net/ohmygirl/article/details/6924229。比較經典,不需過多解釋。