有時我們希望可以找到乙個全排列的下m個全排列,而不僅僅是下乙個,就這樣出現了字典中介法。
洛谷1088 火星人
題目描述
求排列a[1],a[2],a[3],……,a[n]之後的第m個全排列。
輸入格式
共三行。
第一行乙個正整數n(1 <= n <= 10000)。
第二行乙個正整數m(1 <= n <= 100)。
下一行是1到n這n個整數的乙個排列,用空格隔開。
輸出格式
n個整數,表示第m個全排列。每兩個相鄰的數中間用乙個空格分開。
輸入輸出樣例
輸入
531
2345
輸出
124
53
這裡對於乙個全排列我們需要乙個中介數,舉個例子假設我們要求839647521的下100個全排列,這裡我們生成其對應的中介數:8後面比8小的有7個數,3後面比3小的有2個數,9後面比9小的有6個數,6後面比6小的有4個數……mid[i]表示a[i]後面比a[i]小的數的個數,得到中介數mid = 726423210。
我們可以發現對於mid[i]最大為(n - i),因為位置i後面有(n - i)個位置,只有當後面所有數都比它小的時候mid[i] = (n - i)。這樣我們就可以發現中介數mid除去最後乙個0就是乙個遞增進製數(第i位的進製是(n - i + 1),最後一位是二進位制,因為一進製恒為0),這樣我們讓這個遞增進製數72642321加上100,就是72652011
遞增進製(72642321) + 十進位制(100)
最後得到遞增進製(72652011),這也就是下100個排列的中介數了。
這裡要加個特判:如果mid[0]大於0了,代表這個排列比排列n,(n - 1),……,1還大,那麼根本沒有這種排列,所以直接返回false就行了。
用遞增進製數再求出排列:中介數mid[i]表示第i位右側比a[i]小的數的個數,因此我們每次從1開始數(mid[i] + 1)個,這裡選過的數字不能再算在其中,那麼數到的這個數字就是a[i]的值,下面是例子。
最後算一下演算法時間複雜度:最多的有兩重迴圈,所以時間複雜度是o(n^2)。
# include
# include
# include
# include
using
namespace std;
const
int n_max =
10000
;int n, m;
int a[n_max +10]
;int mid[n_max +10]
;// mid[i]表示排列中第i位後面比a[i]小的數的個數
bool flag[n_max +10]
;// flag[i]表示新排列中數字i是否被使用
bool
permutation()
if(mid[0]
>0)
return
false
;for
(int i =
1; i <= n; i++
) flag[pos]
=true
; a[i]
= pos;
}return
true;}
intmain()
演算法 全排列問題 字典序法
求乙個排列的下乙個排列我們有暴力的n進制法,當然也就有效率較高的字典序法。洛谷1088 火星人 題目描述 求排列a 1 a 2 a 3 a n 之後的第m個全排列。輸入格式 共三行。第一行乙個正整數n 1 n 10000 第二行乙個正整數m 1 n 100 下一行是1到n這n個整數的乙個排列,用空格...
全排列演算法之字典序法
字典序演算法如下 設p是1 n的乙個全排列 p p1p2.pn p1p2.pj 1pjpj 1.pk 1pkpk 1.pn 1 從排列的右端開始,找出第乙個比右邊數字小的數字的序號j j從左端開始計算 即 j max index return index 在pj的右邊的數字中,找出所有比pj大的數中...
全排列之字典序法
1 對於輸入的字典序排列,反向查詢第一對滿足a j 2 仍舊反向查詢第乙個下標k,使得 a j 3 交換a j 和a k 4 翻轉a j 1 a end 此法能適應有重複元素的系列 如下 include include using namespace std int cmp const void a...