演算法總結 排列組合與子集問題 排列組合與子集問題

2021-07-04 12:10:46 字數 3395 閱讀 3440

1.組合問題:

問題描述:對於一組各不相同的數字,從中任意抽取1-n個數字,構成乙個新的集合。求出所有的可能的集合。例如,對於集合,其所有子集為,,,,,, 給定乙個陣列(元素各不相同),求出陣列的元素的所有非空組合(即陣列的所有非空子集)

解法一:位向量法。用乙個輔助陣列表示各個元素的狀態。1表示在集合中,0表示不在陣列中。遞迴地求解所有的子集。

演算法描述如下://這裡的演算法對空集也輸出了,可修改之使得只輸出非空集合。

[cpp]view plain

copy

print?

void

getsubset(

int*a,

int*b,

intn,

intk)else

if(i==(n-1))\n"

);  

}  if

(b[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

[cpp]view plain

copy

print?

void

print_subset(

intn,

ints)  

printf("}\n"

);  

}  void

subset(

intn)  

}  

只需要呼叫subset(n)即可輸出1->n個數字的所有組合。或者修改輸出部分為輸出乙個特定集合的組合。

2.。排列問題。

給定一組不相同的數字。求出這n個數字的各種排列形式。稱為排列問題。

解法一:暴力搜尋,對於乙個全排列問題,相當於搜尋乙個具有n個n-1叉數的深林。暴力搜尋之,得到所有的全排列形式。**如下:

[cpp]view plain

copy

print?

#include 

#include 

void

output(

int*a,

intn)  

printf("\n"

);  

}  void

perm(

int*a,

int*b,

intn,

intk)else

}  if(flag)  

}  }  

}  int

main()  

perm(a,b,3,0);  

}  

解法二:模擬回溯法生成排列的過程,對於已知的乙個序列,如果交換其中兩個元素的,會得到新的序列。思路類似於生成組合問題。演算法描述如下:

[cpp]view plain

copy

print?

void

permutation(

int*a, 

intn,

intk)  

printf("}\n"

);  

return

;  }  

for(

inti = k;i

swap(&a[i],&a[k]);  

permutation(a,n,k+1);  

swap(&a[i],&a[k]);  

}  }  

解法三:c++ 中stl中next_permutation()方法。注意這種方法要得到所有的排列,需要原始陣列為遞增有序的,可先對其qsort()

[cpp]view plain

copy

print?

doprintf("}\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=。n對集合的笛卡爾積由此遞迴定義得到。

[php]view plain

copy

print?

<?php  

/** 笛卡爾(descartes)乘積又叫直積。設a、b是任意兩個集合,在集合a中任意取乙個元素x,在集合b中任意取乙個元素y,組成乙個有序對(x,y),

* 把這樣的有序對作為新的元素,他們的全體組成的集合稱為集合a和集合b的直積,記為a×b,即a×b=。

* @author xuzuning

*/function

descartes()   

$a= 

array_shift($t

);  

if(! 

is_array($a

))   

$a= 

array_chunk($a

, 1);

//目的是分解成array(a)這樣的形式,便於後面的合併

dowhile($t

);  

return

$r;  

}  $arr

= array

(  array

('a1'

,'a2'

,),  

'b',  

array

('c1'

,'c2'

,),  

array

('d1'

,'d2'

,'d3'

)  );  

$r= descartes( 

$arr

);  

?>  

上述求全排列和子集的方法,稱為回溯法。對於回溯法,有乙個基本的框架(模式):

[plain]view plain

copy

print?

void backtrack(int n,int k)  

else  

}  

利用這個模式可以寫出例如組合,全排列,子集,八皇后等問題。

八皇后問題的解法:比較經典,不需過多解釋。

排列 組合 子集

目錄 result def backtrack 路徑,選擇列表 if 滿足結束條件 result.add 路徑 return for 選擇 in 選擇列表 做選擇backtrack 路徑,選擇列表 撤銷選擇 排列問題,講究順序 即 2,2,3 與 2,3,2 視為不同列表時 需要記錄哪些數字已經使用...

演算法總結 排列組合

46 全排列 關鍵點 使用used記錄已使用的用數字 class solution def permute self,nums list int list list int self.res self.used false len nums self.generatearrange nums,0,r...

排列組合問題總結

根本思想還是組合數學的加法原則,將乙個狀態分成幾個不相交的狀態,然後用加法原則加起來即可 如果 n m c n 1,m 1 c n 1,m 1 c n 1,m 1 否則 n000 分析 使用插板法 n個球中間有n 1個間隙,現在要分成m個盒子,而且不能有空箱子,所以只要在n 1個間隙選出m 1個間隙...