去重全排列
#include<bits/stdc++.h>
using namespace std;
int num[100],cnt;
void fun(int pos,int n);
int judge(int l,int r);
int main()
{
int i,n;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&num[i]);
fun(1,n);
system("pause");
return 0;
}
int judge(int l,int r)
{
for(;l<r;l++)
if(num[l]==num[r]) return 1;
return 0;
}
void fun(int pos,int n)
{
if(pos==n+1)
{
cnt++;
for(int i=1;i<=n;i++)
printf("%d%c",num[i],i==n?'\n':' ');
return ;
}
for(int i=pos;i<=n;i++)
{
if(judge(pos,i)) continue;
swap(num[pos],num[i]);
fun(pos+1,n);
swap(num[pos],num[i]);
}
}
康托
定义
康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。
康托展开
令X为全排列中的次序,num[]为原数组,n为全排列中数字的个数,为num[j] (j>i)小于num[i]的个数。
则
例:有(1,2,3,4,5)5个数的全排列中,计算34152的康托展开值。
通过定义可知,a[5] = 2 (1<3,2<3),a[4] = 2 (1<4,2<4),a[3] = 0 (无),a[2] = 1 (2<5),a[1] = 0 (无)。
又因为 n = 5,则X=2*4! + 2*3! + 0*2! + 1*1! + 0*0! = 61
所以在全排列中比34152小的有61个,即34152在全排列中排第62位(61+1=62)。
逆康托展开
因为康托展开是一个全排列到一个自然数的双射,所以可以通过61计算出34152。
有,得出
例:
61/4! = 2...13 则a[5] = 2,num[5]=3
13/3! = 2...1 则a[5] = 2,num[4]=4
1/2! = 0...1 则a[5] = 0,num[4]=1
1/1! = 1...0 则a[5] = 1,num[2]=5
则num[1]为剩下的数,num[1]=2
所以前面有61个排列,即第62个全排列为34152。
下一个全排列
26458173的下一个全排列为26458317。
步骤:
①从后往前找到第一个 的位置 (即样例中为1<7)
②从后往前找到第一个大于的数 x ,交换
和 x的位置 (
=1,x=3),得到26458371
③将位置 i 以后的数反转,得到26458317。
上一个全排列的求法与下一个全排列类似,自行解决。?