全排列 & 康托展开

去重全排列

#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。

上一个全排列的求法与下一个全排列类似,自行解决。?

转载于:https://www.cnblogs.com/VividBinGo/p/11505976.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值