康托展开

[总结] 康托展开及其逆运算

牧云@^-^@ 提交于 2020-03-25 02:54:41
这里先贴一道 例题 我们先科普一下康托展开 定义: X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! ai为整数,并且0<=ai<i(1<=i<=n) 简单点说就是,判断这个数在其各个数字全排列中从小到大排第几位。 比如 1 3 2,在1、2、3的全排列中排第2位。 康托展开有啥用呢? 维基: n位(0~n-1)全排列后,其康托展开唯一且最大约为n!,因此可以由更小的空间来储存这些排列。由公式可将X逆推出对应的全排列。 它可以应用于哈希表中空间压缩, 而且在搜索某些类型题时,将VIS数组量压缩。比如:八数码,魔板等题 康托展开求法: 比如 2 1 4 3 这个数,求其展开: 从头判断,至尾结束, ① 比 2(第一位数)小的数有多少个->1个 就是1,1*3! ② 比 1(第二位数)小的数有多少个->0个 0*2! ③ 比 4(第三位数)小的数有多少个->3个 就是1,2,3,但是1,2之前已经出现,所以是 1*1! 将所有乘积相加=7 比该数小的数有7个,所以该数排第8的位置。 1234 1243 1324 1342 1423 1432 2134 2143 2314 2341 2413 2431 3124 3142 3214 3241 3412 3421 4123 4132 4213 4231 4312 4321

康托展开(数论)

眉间皱痕 提交于 2020-03-25 02:53:44
康托展开 标签: 数学方法——数论 阅读体验: https://zybuluo.com/Junlier/note/1174122 一、定义 来自网络的定义: 康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。 设有n个数 \((1,2,3,4,...,n)\) ,可以有组成不同( \(n!\) 种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。 通俗来讲: 假设有一个 排列 {1,2,3,4,5},需要你在它的全排列中,找到排名第m的那个排列 全排列的顺序就是字典序越来越大的排列,和我们的 next_permutation() 函数的顺序一样 二、怎么实现? 首先,放一个很重要的公式(暂时不需要理解,后面慢慢就懂了): \[X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! \] 其中 $ ! $ 是阶乘的意思 我们再看一个表格 你先不管它的康托展开那一栏,根据后面的讲解再看 排列组合 名次 康托展开 123 1 0 * 2! + 0 * 1! + 0 * 0! 132 2 0 * 2! + 1 * 1! + 0 * 0! 213 3 1 * 2! + 0 * 1! + 0 * 0! 231 4 1 * 2! + 1 * 1! + 0 * 0! 312 5 2 * 2!

逆康托展开

妖精的绣舞 提交于 2020-02-27 09:43:37
问题 A: 第Q个全排列 题目描述 现有一个长度为n的集合S,集合S里的元素为 [1,2,3……n]。按照高中数学的知识,我们显然可以知道,集合里的元素的排列共有 n! 种。现在给你集合长度n以及一个正整数 Q,请你找出集合元素组成的第Q个的排列。 输入 输入数据由多组测试样例组成,每组测试样例第一行分别输入两个正整数n ( 1 <= n <= 9 ),Q( 1 <= Q <= n! ) 输出 输出第Q个的排列,元素之间没有空格 样例输入 2 2 5 10 样例输出 21 13452 提示 按照题目中第一组测试样例举例: 按从小到大顺序列出所有排列情况,当 n = 2 时, 所有排列如下: 1 2 2 1 所以第二大的排列是 21 AC代码: # include <bits/stdc++.h> using namespace std ; int f [ 15 ] ; int s [ 10 ] = { 0 , 1 , 2 , 6 , 24 , 120 , 720 , 5040 , 40320 , 362880 } ; int main ( ) { int n , q ; while ( cin >> n >> q ) { int ans = 0 , m = q - 1 ; for ( int i = 0 ; i < n ; i ++ ) { f [ i ] = i + 1 ; }

康托展开

邮差的信 提交于 2020-01-29 17:01:14
康拓展开与逆康拓展开 一. 简介   首先解释一下,所谓的康拓展开,就是能够通过一个式子,得到一个排列在所有排列中的按字典序排好后的位次。而逆康托展开,则是给出排列的位次,能够计算出排列是什么。   下面先给出康拓展开的公式 其中a i 为整数 ,并且 0≤a i <i ,1≤ i ≤ n。    a i 表示原数的第 i位在当前未出现的元素中是排在第几个 。   康拓展开是一个双射,因此常用在 hash中,比如八数码问题。   下面我们来演示下康拓展开的具体计算过程 对于一个 1-4的组成的排列{2,3,4,1},那我们应该怎么计算它的康拓展开值rank。 第一个数是 2,后面比它小的就只有1了,rank+=(1*(3)!) 第二个数是 3,后面比它小的也只有1了,rank+=(1*(2)!) 第三个数是 4, 后面比它小的也只有 1了,rank+=(1*1!) 最后一个数是 1,没有比它小的数了, rank+=(0*0!)   所以最终 rank = 9(排列{1,2,3,4}是0)。通常我们都是从1开始,所以最终rank是10。   接下来我们再演示下逆康拓展开。   首先 rank--,rank = 9;   1.9/3!,得1余3,在未选取的数里,2前面正好有一个1个未选取的数,所以第一个数为2。   2.3/2!,得1余1, 在未选取的数里,

leetcode专题训练 60. Permutation Sequence

拈花ヽ惹草 提交于 2020-01-25 16:21:42
这道题用康托展开的思想进行求解。 康托展开运算 其中, a i a_i a i ​ 为整数,并且 0 ≤ a i < i , 1 ≤ i ≤ n 0\le a_i<i,1\le i\le n 0 ≤ a i ​ < i , 1 ≤ i ≤ n 。 a i a_i a i ​ 表示原数的第i位在当前未出现的元素中是排在第几个 z康托展开的逆运算 既然康托展开是一个双射,那么一定可以通过康托展开值求出原排列,即可以求出n的全排列中第x大排列。 逆康托展开举例 一开始已经提过了,康托展开是一个全排列到一个自然数的双射,因此是可逆的。给出数组(1,2,3,4,5)与位次62,具体过程如下: 由于要算比当前数小的数,所以要将62-1=61 用 61 / 4! = 2余13,说明 a [ 5 ] = 2 a[5]=2 a [ 5 ] = 2 ,说明比首位小的数有2个,所以首位为3。 用 13 / 3! = 2余1,说明 a [ 4 ] = 2 a[4]=2 a [ 4 ] = 2 ,说明在第二位之后小于第二位的数有2个,所以第二位为4。 用 1 / 2! = 0余1,说明 a [ 3 ] = 0 a[3]=0 a [ 3 ] = 0 ,说明在第三位之后没有小于第三位的数,所以第三位为1。 用 1 / 1! = 1余0,说明 a [ 2 ] = 1 a[2]=1 a [ 2 ] = 1

康托展开 & 逆康托展开(计算一个序列的对应的值)

ε祈祈猫儿з 提交于 2020-01-24 16:11:34
1、康拓展开: 一个序列长度为n,是1~n的数字的全排列中的一种,求出这个序列对应的全排列中序列的编号就是康拓展开, 即将一个全拍列的序列转化为一个数字编号。 代码: int cantor(int a[],int n) //序列a的下标从0~n-1 { int ans = 0; for(int i=0;i<n;i++) { int x = 0,c = 1,m = 1; for(int j=i+1;j<n;j++) { if(a[j]<a[i]) x++; m *= c; c++; } ans += x*m; } return ans; } 2、逆康拓展开 将一个数字编号转化为一个长度为n的全排列的序列,eg:x = 1时代表的序列为1 2 3 4 5,一次类推。 代码: int fac[120],b[120]; bool vis[120]; void decantor(int x,int n) { fac[0] = 1; for(int i=1;i<=n;i++) fac[i] = fac[i-1]*i,vis[i] = false; x--;//如果从1开始 for(int j,i=0;i<n;i++) { int t = x/fac[n-1-i]; for(j=1;j<=n;j++) if(vis[j] == false){ if(t == 0) break; t--; } b

【数学】康托展开 &amp;&amp; 康托逆展开

匿名 (未验证) 提交于 2019-12-02 23:48:02
  (7.15)康托展开,就是把全排列转化为唯一对应自然数的算法。它可以建立1 ~ n的全排列与[1, n!]之间的自然数的双向映射。 1、康托展开:   尽管我并不清楚康托展开的原理何在,这个算法的过程还是比较好记的。正确性之后有机会询问下学长。   如果从1开始给全排列的排名从大到小编号的话(从0开始也可,建立的是与[0, n!-1]的映射,本质相同),定义rk为排名,a是排列数组,排列有n位(最低位是第0位),那么有公式   rk - 1 = cnt[n-1] * (n-1)! + cnt[n-2] * (n-2)! + ... + cnt[0] * 0!     其中cnt数组的含义是未统计的数字中,小于a[i]的数字有多少个。   举例:计算排列3 4 2 1对于{1, 2, 3, 4}的排名   首先取出最高位(第三位),小于数字3的数有两个,所以cnt[3] = 2,rk += 2 * 3!,rk = 12。   然后取出4,小于4的数有三个,但是3已经被统计过了,所以cnt[2] = 2,rk += 2 * 2!,rk = 16.   取出2,小于2的只有1,cnt[1] = 1,rk += 1 * 1!,rk = 17。   最后由于除第0位本身外已经没有数了,cnt[0]恒等于0。所以3 4 2 1的排名为18。 代码: //阶乘   

luogu P5367 【模板】康托展开 / P2518 [HAOI2010]计数

泪湿孤枕 提交于 2019-12-02 04:57:15
今天学习 康托展开 和 可重集康托展开。。。 康托展开。。就是给你一个1~n全排列,问它的排名。。 例如:x1, x2 , x3 , x4 , x5 是一个1~5的全排列,现在我们要求它的排名。。 从第1位开始枚举。。 然后枚举j,当然这个j要< xi ,并且不能在x(1~i-1)中出现过。 发现当 j < xi 时 ,后面无论怎样填,都会比所求排列的字典序小。。所以就是! (len-i) 。累加到答案中。 枚举完j后,就令第i位为xi,将xi标记上即可,后面枚举 j 时就不能在枚举到 xi了。 考虑优化枚举 j 这个过程。。 开个树状数组。记录一下前面1~i-1出现过那些数,合法的j的个数即为xi-1-ask(xi - 1)啦,直接乘上 ! (len-i) 即可。。 最后add(xi)。。 然后就是蛤OI的这道计数题啦。。 只不过是可以有相同的元素罢了。。 把上面枚举j统计答案乘!(len-i)的过程改为乘上后面剩余元素可重集的全排列即可。。 其他的大致相同。。这个玩意就不能用树状数组了,只能老老实实枚举 j 。。 由于这个题它TM不取模,,搞得求可重集的全排列的过程很难搞。。 虽然最后结果不会爆long long 但中间也可能会爆。。 所以我们就不能直接按求可重集的全排列的公式来求了。。 用组合数。。而由于我是个蒟蒻,不会用符号,就没有办法演示了。。看代码吧。。

LOJ167 康托展开 题解

主宰稳场 提交于 2019-11-30 07:17:07
题面 康托展开: 康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的名次,因此是可逆的。 X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0! A[i] 指的是位于位置i后面的数小于A[i]值的个数,后面乘的就是后面还有多少个数的阶乘 这个算出来的数康拖展开值,是在所有排列次序 - 1的值,因此 X +1即为在全排列中的次序 问:虽然知道了康托展开的方法,但怎样知道排名求排列呢? 答:那就使用逆康托展开!; 逆康托展开: 前面已经说到康拖展开是从序列到自然数的映射且是可逆的,那么逆康拖展开便是从自然数到序列的映射。 举例子: 在(1,2,3,4,5) 给出61可以算出起排列组合为34152 具体过程如下: 用 61 / 4! = 2余13,说明 ,说明比首位小的数有2个,所以首位为3。 用 13 / 3! = 2余1,说明 ,说明在第二位之后小于第二位的数有2个,所以第二位为4。 用 1 / 2! = 0余1,说明 ,说明在第三位之后没有小于第三位的数,所以第三位为1。 用 1 / 1! = 1余0,说明 ,说明在第二位之后小于第四位的数有1个,所以第四位为5。 来源: https://www.cnblogs.com/kamimxr/p/11571579