減治求有重複元素的全排列

2021-09-22 20:23:13 字數 1696 閱讀 4771

求n個元素的全排列的所有解可以用減治法:每次拎出乙個數做字首,對剩下的元素再求全排列,直至只剩乙個元素。**源自《演算法分析與設計(王曉東)》,複雜度o(n!)

1

//輸出k~m的所有全排列

2void perm(int k,intm)3

else

1017

}18 }

以上沒有考慮有重複元素的情況。簡單地想,若有元素重複,則這個元素只需被拎出來做一次字首就好了,那麼只需在拎字首前判斷這個數是否已被拎出來過。

那麼如何高效地判斷呢?可以先對所有元素排序,這樣重複元素分布在相鄰位置,一趟掃瞄,只對與前驅不同的元素做處理。

**只需在k~m的迴圈內加一句 if(i>k&&list[i]==list[i-1]) continue;

下面再來看一道類似的問題

uva11076  

由0~9中n個數字(可重複)組成的陣列,把每個全排列看成乙個n位數,求所有全排列的和。

根據全排列的性質,將所有全排列按行寫出後,發現每一列「所有數字的和s」都相等,因此我們可以只求任一列的和然後進行n次的*10累加。

對於每個數字k,我們已知它在陣列**現的次數cnt[k](即k有cnt[k]-1個副本),但要對一列求和(不妨求第1列),我們需要知道每個數字在這一列出現的次數cnt_2[k]。由於全排列是沒有相同的,那麼「第1位是k」的次數就等價於「去掉k後剩餘元素的全排列的個數」,至此,問題轉化為上面的減治法求全排列。

本題只需求全排列個數(值)而不必輸出具體排列(解),因此可以用高中排列組合的經典做法:先視為無重複全排,再除以所有重複元素的排列個數。

做完發現此題由於數字是0~9所以天然地把元素排好序並記錄好重複次數了,因此對每個數字k只計算一次,計算時將k的個數看作cnt[k]-1即可。

**如下:

1 #include 2 #include 3

using

namespace

std;

45 typedef unsigned long

long

ull;6//

只有0~9

7 ull fac=;

8int a[13];9

int cnt[10];//

出現的次數

10int cnt_2[10];//

在所有全排列的任一列**現的次數

11ull sum,ans,s;

12int

n;13

14int

main()

1526 s=0;27

for(int i=0;i<=9;i++)

28

37 s+=i*cnt_2[i];38}

39 ans=0;40

for(int i=0;i)

4145 printf("

%llu\n

",ans);46}

47return0;

48 }

注:之前自己把自己搞暈過,去重實現不了當成是swap的問題,認為簡單交換i與k會破壞「重複元素集中分布」或「有序序列」這兩個條件,但再分析發現並沒什麼關係。。。減治啊減治,字首被拎走就對字尾的全排列沒影響了,只要保證每次迴圈中重複元素不被拎到同一位置就可以。

從問題本身和演算法思想出發去分析還是很有意思的~演算法知識博大精深,希望自己多練習多積累,早日不再那麼水~~~

有重複元素的全排列

題目描述 集合s中有n個元素,其中的元素可能重複,設計乙個演算法,計算出s的不同排列字元全部由小寫字母組成,輸出按照字典序輸出 n 9輸入 第一行乙個整數n 第二行乙個字串包含n個字母輸出 所有的全排列 最後一行輸出個數 樣例輸入 4 aacc 樣例輸出 aacc acac acca caac ca...

有重複元素的全排列

題目描述 description 輸入n 10 個小些字母 可能重複 輸出n個字元的全部排列。input abaab output 1 aaabb 2 aabab 3 aabba 4 abaab 5 ababa 6 abbaa 7 baaab 8 baaba 9 babaa 10 bbaaa 演算法...

全排列 不含重複元素

總結定義 從n個不同元素中任取m m n 個元素,按照一定的順序排列起來,叫做從n個不同元素中取出m個元素的乙個排列。當m n時所有的排列情況叫全排列。示例 對陣列或者字串進行全排列時,一般要求得出所有的排列結果。排列結果中的每個元素來自於原始陣列,數量和內容與原始陣列相同,只是元素的位置發生了改變...