康拓拓展和逆拓展解決全排列問題

2021-09-25 07:22:56 字數 1732 閱讀 1131

在解決排列問題時,一般有以下問題:

給你乙個排列,問你是第幾個排列

給你乙個排列,問下乙個排列是多少

給你乙個排列,問前乙個排列是多少

給你乙個數i,問第i個排列是多少

一般暴力的話使用遞迴加回溯都能解決,但是時間複雜福比較高,遞迴多了棧也會溢位。

這裡使用康拓展開的思想,具體思路。

看不懂沒關係,繼續看就知道啦。

假如給你乙個排列[2,4,3,1],問你是第幾個排列。這時你可以想想,以1開頭的所有排列你是不是可以手動算出來,就是3!,沒錯,看到這裡我當時頓悟,後面的就不看了,直接上手寫**。當時就不知道有康拓思想,雖然**編寫的有點慢,卡在了乙個問題上,但是最後還是通過了。繼續:

注意count += 3! = 6;1位於第一位排列的所以可能就是數就是6,不用算後面的,然後就是2排在第一位了。

這時看第二位4,按照從小到大的順序,第二位應該是1,3。所以要計算以1,3位於第二位的全排列數,第二位的全排列數是2!,也就是2*2!。即count += 2*2! = 10;這時按照順序第二位不是1,3(這裡有2位)就是4了。(這裡2被忽略,因為被使用過了)

再看第三位數是3;按照從小到大的順序,應該是1(只有乙個了),但是現在是3,所以計算數字1位於第三位的全排列數,是1*1! = 1,所以count += 11;

再看第四位是1,最後一位了,必須是他了,排在他前面的都被使用完了,所以為0,最後一位是0!也就是0*0! = 0

最後由於是從0開始的,count= 11,也就是在他前面有11個排列,那麼該排列就是第12個排列。

這就對應上面的公式,

**如下:

//對前 10 個自然數(0 ~ 9)的階乘存入表

//以免去對其額外的計算

const int fact[10] = ;

/** * @brief 康拓展開

* * @param[in] permutation 輸入的乙個全排列

* @param[out] num 輸入的康拓對映,即是第幾個全排列

*/int contor(const vector& permutation)

return num + 1;

}

這個逆排序是求解給定乙個數rank,獲得第rank個排序的結果。這裡另rank = 10;

首先rank - 1,因為求解是從0開始開始的。

然後rank  = rank%(n-1)!,即 rank= 3

重複上一步,3/2! = 1...1,同樣說明位於第2位的數在原始排列[1,2,3,4]中取第乙個未取到的是不行的,那就是第二個3,因為2被取過了。然後rank = rank%(n-1)! = 1

再重複,1/1! = 1...0,說明位於第3位的數在原始排列中取第乙個未取到的是不行的,那就第二個4,。。。後面就沒了。

**如下:

//對前 10 個自然數(0 ~ 9)的階乘存入表

//以免去對其額外的計算

const int fact[10] = ;

/** * @brief 逆康拓展開

* * @param[in] bits 給定全排列的使用數字個數

* @param[in] num 給定全排列的次位

* @param[out] permutation 輸出對應的全排列

*/vectorrevcontor(int bits, int num) }}

return permutation;

}

康拓展開 康拓逆展開

康拓展開 已知有一集合a包含n個不同的元素,其中 k1,k2,k3.kn 2 是a的乙個排列。假設此排列為a按字典序從小到大排列的排列中的第x個排列,則x a1 n 1 a2 n 2 an 2 1 an 1 0 其中ai為ki 1.kn中比ki小的數的個數 例如 3214是1234的第2 3 1 2...

康拓展開與康拓逆展開

x a n n 1 a n 1 n 2 a i i 1 a 1 0 其中a i 為當前未出現的元素中是排在第幾個 從0開始 這就是康托展開。公式把乙個整數x展開成如下形式 x a n n 1 a n 1 n 2 a i i 1 a 2 1 a 1 0 其中a i 為 當前元素 在 所有未出現的元素 ...

康拓展開和逆康拓展開

康托展開就是一種特殊的雜湊函式 把乙個整數x展開成如下形式 x a n n a n 1 n 1 a 2 2 a 1 1 其中,a為整數,並且0 a表示1,2,3,n的排列如 按從小到大排列一共6種,就是123 132 213 231 312 321 代表的數字 1 2 3 4 5 6 也就是把10進...