下一个排列-cnblog
下一个排列
题意:给定一个长度为\(n\)的排列\(p\),现在需要求出\(p\)的下一个排列(字典序大于当前排列的最小字典序排列)。
比如\([1,3,4,2]\),我们去找大于这个排列的下一个排列,根据字典序比较规则,越靠前,且数字越大,字典序越大。
那么显然我们考虑一个后缀\(i+1\),如果后面是一个完全降序的序列,那么调整这个后缀显然无法得到一个字典序更大的排列,所以我们需要从后往前找到第一个\(a[i]<a[i+1]\),我们需要从后缀中找到一个数来替换\(a[i]\),同时为了使得增幅最小,所以我们从后缀中找到大于\(a[i]\)的最小的\(a[j]\),然后交换\(a[i],a[j]\),之后显然我们可以将后缀\(i+1\)逆序一下,这样就可以得到下一个更小的排列。
考虑这个例子\([3,5,4,2,1]\),显然后缀\([5,4,2,1]\)无法得到下一个排列,我们只能增大第一个位置,那么找到第一个大于\(3\)的数4,这时候交换这两个,得到\([4,5,3,2,1]\),显然我们可以将\([5,3,2,1]\)重排得到\([1,2,3,5]\),这样得到下一个排列\([4,1,2,3,5]\)。
我们再考虑一下如何得到上一个排列呢,是不是也就是这个的逆过程:
- 找到一个递增的后缀,之后将其逆序,\([4,5,3,2,1]\);
- 之后找到小于4的最大的那个,将其进行交换得到\([3,5,4,2,1]\);
- 这样就得到了上一个排列;
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool next_permutaion(vector<int>& a){
int n=a.size();
int i=n-2;
while(i>=0&&a[i]>=a[i+1]){
i--;
}
if(i<0) return false;
//找到了一个a[i]>a[i+1]
int j=n-1;
while(j>i&&a[j]<=a[i]){
j--;
}
swap(a[i],a[j]);
reverse(a.begin()+i+1,a.end());
return true;
}
bool prev_permutation(vector<int>& a){
int n = a.size();
int i = n - 2;
// 找第一个 a[i] > a[i+1]
while(i >= 0 && a[i] <= a[i+1]){
i--;
}
if(i < 0) return false;
int j = n - 1;
while(j > i && a[j] >= a[i]){
j--;
}
swap(a[i], a[j]);
reverse(a.begin() + i + 1, a.end());
return true;
}
int main(){
int n;
cin >> n;
vector<int> p(n);
for(int i=0;i<n;i++){
cin >> p[i];
}
do{
for(int i=0;i<n;i++){
cout << p[i] << " ";
}
cout << endl;
}while(next_permutation(p));
return 0;
}
而要得到当前排列的下\(k\)个排列,我们可以使用康托展开和逆康托展开,先得到当前排列的排名,之后加上\(k\),最后将排名转化成排列。

浙公网安备 33010602011771号