在解決排列問題時,一般有以下問題:
給你乙個排列,問你是第幾個排列
給你乙個排列,問下乙個排列是多少
給你乙個排列,問前乙個排列是多少
給你乙個數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進...