含义

  康托展开是一个全排列到一个自然数双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

原理

  X = s1(n-1)! + s2(n-2)! + s3(n-3)! + …… + sn-1 * 1! + sn * 0!

  其中si表示在第i位右边比ai小的数的个数。

  我们现在用sl表示第i位左边比ai小的数的个数,sr表示第i位右边比ai小的数的个数,显然可以得到如下等式:
  ai = sl + sr + 1

  故公式中的si可以用上述等式计算:sr = ai - sl -1

  依照上述原理,则{1,2,3,4,5}的一种全排列{3,4,1,5,2}可以映射为{2,2,0,1,0}

  根据公式,该集合实际上表示的就是一个变进制数。

  简便计算:X = ((s1 * (n-1) + s2) * (n-3) + s3) * (n-3) + ……

康托展开

  暴力  O(n2):

  为了便于讲解下面的线段树优化,此处选择维护vis数组的方式(当然直接比大小也是一样的)。

  vis[j]用以记录j是否已出现,未出现为0,已出现为1。故a[i]左边比其小的数的个数就是vis[j]的和(j<a[i])。

  计算ans时,由于最后一位固定是0,故只需计算到n-1位即可。

  而所有全排列中比该序列小的有ans个,故该序列排在第ans+1位。

  代码如下:

int Power_Cantor()
{int ans=0;for(int i=1;i<=n-1;i++){scanf("%d",&a[i]);int sum=0;for(int j=1;j<=a[i];j++)sum+=vis[j];vis[a[i]]=1;a[i]-=sum+1;ans=(ans+a[i])*(n-i);}return ans+1;
}

  线段树优化  O(nlogn):

  我们用线段树结构存储vis数组,即可实现logn时间复杂度内求出结果。

  代码如下:

int Cantor()
{int ans=0;for(int i=1;i<=n-1;i++){int now=a[i];now-=query(1,1,n,1,a[i])+1;update(1,1,n,a[i],1);ans=(ans+now)*(n-i);}return ans+1;
}

逆康托展开

  首先是将所给的排列位数转变为我们所需的变进制数:

  仍以{3,4,1,5,2}为例,其康托展开值为61:

  用 61 / 4! = 2余13,则a[1] = 2,即首位右边比首位小的数有2个,所以首位为3。

  用 13 / 3! = 2余1,则a[2] = 2,即在第二位之后小于第二位的数有2个,所以第二位为4。

  用 1 / 2! = 0余1,则a[3] = 0,即在第三位之后没有小于第三位的数,所以第三位为1。

  用 1 / 1! = 1余0,则a[4] = 1,即在第四位之后小于第四位的数有1个,所以第四位为5。

  最后一位自然就是剩下的数2。

  通过以上分析,所求排列组合为 34152。

  依然是用线段树维护vis数组,建树时每一位都先赋值为1,表示所有数均未出现。

  设对于当前这一位i,变进制数为a[i],要求的数为x。目标是要在未出现的数中找比x小的数的个数为a[i]个的数的位置,相当于在区间[1,x]中找a[i]+1。

  此处将a[i]+1记为s[i]。用二分查找x的位置:对于每一位s[i],先求出左子树比x小的数的个数为sum,再看s是否有s[i]个:若有,说明结果在左子树,则继续往左子树找s[i];若没有,则往右子树找s[i] - sum。

  查找完后将vis[x]的值修改为0。

  代码如下:

int search(int num)
{int l=1,r=n;while(l<r){int mid=l+r>>1;int find=query(1,1,n,l,mid);if(find>=num) r=mid;else{l=mid+1;num-=find;}}return r;
}void R_Cantor(int num)
{num--;  memset(a,0,sizeof a);build_tree(1,1,n);for(int i=1;i<=n;i++){a[i]=num/(jc[n-i]);a[i]=search(a[i]+1);update(1,1,n,a[i],0);num%=jc[n-i];}for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
}

附完整代码

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5+10;int n;
int a[N],tree[3*N];
int jc[10]={1,1,2,6,24,120,720,5040,40320,362880}; void out()
{for(int i=1;i<=14;i++){printf("tree[%d] = %d\n",i,tree[i]);}puts("");
}void build_tree(int node,int start,int end)
{if(start==end){tree[node]=1;return;}int mid=start+end>>1;int left=2*node;int right=2*node+1;build_tree(left,start,mid);build_tree(right,mid+1,end);tree[node]=tree[left]+tree[right];
}void update(int node,int start,int end,int idx,int val)
{if(start==end){tree[node]=val;return;}int mid=start+end>>1;int left=2*node;int right=2*node+1;if(idx<=mid)update(left,start,mid,idx,val);else update(right,mid+1,end,idx,val);tree[node]=tree[left]+tree[right];
}int query(int node,int start,int end,int l,int r)
{if(end<l || start>r)return 0;else if(start>=l && end<=r)return tree[node];int mid=start+end>>1;int left=2*node;int right=2*node+1;return query(left,start,mid,l,r) + query(right,mid+1,end,l,r);
}int Cantor()
{int ans=0;for(int i=1;i<=n-1;i++){int now=a[i];now-=query(1,1,n,1,a[i])+1;update(1,1,n,a[i],1);ans=(ans+now)*(n-i);}return ans+1;  //所有全排列中比该序列小的有ans个,故该序列排在第ans+1位
}int search(int num)
{int l=1,r=n;while(l<r){int mid=l+r>>1;int find=query(1,1,n,l,mid);// 先看左子树中未出现的比要求的数小的数的个数够不够num个 // 若足够,则继续往左子树找num // 若不够,则继续往右子树找 (num-已找到的个数) if(find>=num) r=mid;else{l=mid+1;num-=find;}}return r;
}void R_Cantor(int num)
{num--; //该序列排在第num位,故比其小的全排列有num-1个。 memset(a,0,sizeof a);build_tree(1,1,n);for(int i=1;i<=n;i++){a[i]=num/(jc[n-i]);a[i]=search(a[i]+1);update(1,1,n,a[i],0);num%=jc[n-i];}for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
}int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);memset(tree,0,sizeof tree);int ans=Cantor();//    R_Cantor(62);
printf("%d\n",ans);return 0;
}

转载于:https://www.cnblogs.com/ninedream/p/11521141.html

基本算法——康托展开与逆康托展开相关推荐

  1. 康托展开和逆康托展开

    简述 康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩.设有n个数(1,2,3,4,-,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素 ...

  2. 康拓展开与逆康拓展开

    康托展开与逆康托展开   康托展开据我所理解,应该便是给你一个已知的排列,然后这个排列在全排列中是第几小或者第几大的,一般都是按字典序计算,即为第几小的.康托展开的公式为:X=a[n]*(n-1)!+ ...

  3. 康拓展开和逆康拓展开

    康托展开 公式:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,其中a[i]为当前未出现的元素中(即后面的所有元素中)是排在第几个(从 ...

  4. 数论--康托展开与逆康托展开模板

    ACM常用模板合集 #include<bits/stdc++.h>using namespace std; const int MAX = 13; int Fac[MAX],N; //求出 ...

  5. 数学--数论--康托展开与逆康托展开

    康托展开 可以理解为把一个全排列映射到一个数上面,因为全排列如果按照从小到大或者从大到小,肯定是有一个确定的序列的. 一般是从小到大的序列个数.我们就是要求出这个序列的位置.,想法很简答,就是求出前面 ...

  6. 详解康托展开与逆康托展开

    康托展开 rank=an(n−1)!+an−1(n−2)!+⋯+a10! 再举个例子说明. 在(1,2,3,4,5)5个数的排列组合中,计算 34152的康托展开值. 首位是3,则小于3的数有两个,为 ...

  7. 康托展开与逆康托展开详解

    文章目录 康拓展开 运用 板子 逆康托展开 板子 康拓展开 康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩. 康托展开的实质是计算当前排列在所有由小到大全排列中的名次,因此是可逆 ...

  8. 关于康托展开和逆康托展开详解,及python代码

    在LeetCode上有这样一道题: 60. 第k个排列 给出集合 [1,2,3,-,n],其所有元素共有 n! 种排列. 按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: ...

  9. 康托展开与逆康托展开(bzoj 3301: [USACO2011 Feb] Cow Line)

    康拓展开: 已知序列a1, a2, a3, -, an是1~n的一个排列,求这是1~n全排列中,第几小的排列? ans = ,其中F(i)表示后面n-i个数中比当前小的数的个数 例如 n = 5,序列 ...

最新文章

  1. vim windows linux文件格式转换
  2. 不要上手就学深度学习!超详细的人工智能专家路线图,GitHub数天获2.1k星
  3. 关于java调用Dll文件的异常 %1 不是有效的 Win32 应用程序。
  4. X Window、GNOME和KDE之间的关系
  5. 【Python】简单而不简约:一份Python小抄奉上
  6. Python 第三方模块之 pdfkit
  7. 斑能不能彻底去掉_淡妆能不能只用洗面奶卸掉?
  8. (转)Windows Form Application 读取并修改App.config文件
  9. UAT测试和SIT测试
  10. C 语言中 scanf() 的用法
  11. 力扣 202 快乐数
  12. 双目是个词吗_描写眼睛的词语3个字
  13. 再见了 SELECT * !大厂的 MySQL 查询优化方案,确实牛逼!
  14. python openCv 入门学习-RGB转HSV、掩膜的简单理解(二)
  15. 数学建模算法学习笔记
  16. oracle最小采购量,EBS oracle 批量导入更新MOQ(最小拆分量、采购提前期、最小订购量、最小包装量)...
  17. 抢鞋软件bot服务器系统,抢鞋机器人bot安卓版
  18. Python.random.seed()用法
  19. Unity使用新输入系统InputSystem制作飞机大战Demo(对象池设计模式及应用)
  20. 至爱最不能承受之痛!

热门文章

  1. python中文分词统计_python 实现中文分词统计
  2. python单元测试教程_Python单元测试框架unittest使用方法讲解
  3. 查python的软件_Python制作天气查询软件【python实战必学】
  4. java后端服务运行原理_web服务的后台工作原理
  5. linux使用oracal11g详细过程,很给力的linux下Oracle11g的安装过程及图解.doc
  6. Least-Squares Fitting of Two 3-D Point Sets
  7. 数据集如何影响作物病害识别的有效性
  8. 深度学习自动调参工具,NNI使用
  9. win10无法修改mac地址_路由器无线MAC地址过滤如何设置
  10. thttpd支持php吗,轻量型thttpd+php5