空间压缩神器——康拓展开模板!!!空间压缩神器——康拓展开模板!!!空间压缩神器——康拓展开模板!!!

题目大意:

输入一个排列(长度小于50),输出它是第几个排列! 第一个排列为1。


分析:

求一个数字不重复的数列在它的全排列中排第几位,这就是康拓展开所求。
如:1 2 3 4 5 在这个数列中排第0位,而1 2 3 5 4排第1位。
但是如果直接全排列排出来一个一个去数,未免太慢太没逼格的了,因此就有了康拓展开的一道标准公式:

康拓=a[n] * (n-1)!+a[n-1](n-2)!+a[n-2](n-3)!+…+a[1]*0!

其中,a[ ]数组表示当前下标数在数列中还未进行展开运算的数字中排名第几小(从0开始算第几小,最小就是第0小),什么意思呢?举个例子就明白了:

例如:2 1 3 4 6 5

从第一个数2开始,我们往后找,发现未扩展得数只有 1 小于2,因此2是属于第1小的一个数,所以a[2]=1,把2进行展开(就是套上面的康拓公式):1*(6-1)!=1*120=120

接下来,未被扩展的数字只剩下了: 1 3 4 6 5

第二个数1,从1开始往后找,没有数小于1,于是1是第0小,a[1]=0,把1进行扩展:0 *(6-2)!= 0 * 24=0

剩余数字:3 4 6 5

第三个数3,往后找没有数小于3,第0小,展开:0 * (6-3)!=0 * 6=0

剩余数字:4 6 5

第四个数4,往后找没有数小于4,展开为:0 * (6-4)!=0 * 2=0

剩余数字:6 5

第五个数6,往后找有1个数小于6,展开:1 *(6-5)!=1 * 1=1

最后:5

第六个数5,往后找已经没有数了,所以是第0小,展开后结果为0

所有的数都展开过了,让后将每位数的展开结果累加为:

120+0+0+0+1=121

因此,2 1 3 4 6 5,在为1 2 3 4 5 6 的全排列中,从0开始,序号是121。也就是说,2 1 3 4 6 5是1 2 3 4 5 6的第121个全排列。

再例如:对于序列3,2,5,4,1

对于3:比3小的有1、2,所以3是第2小的,康拓+=2*(5-1)!

对于2:比2小的有1,所以2是第1小的,康拓+=1*(4-1!)

对于5:比5小的有1、2、3、4,但由于2、3已经出现过了,所以目前5是第2小的,康拓+=2*(3-1)!

对于4:比4小的只剩1,所以 康拓+=1*(2-1)!

对于1:已经是最小的,康拓+=0*(1-1)!

最后获得的答案为60

知道了康拓展开的过程,求排列序号就再也不用死打枚举了——这里模板奉上!!!

康拓展开万能模板:康拓展开万能模板:康拓展开万能模板:

为了节省时间,我们先预处理阶乘:
void cal(){fac[0]=1;for(int i=1;i<=9;i++) fac[i]=fac[i-1]*i;
}然后就是康拓展开:
int kangtuo(int* a){int ans=0;for(int i=0;i<=n;i++) label[i]=1;for(int i=0;i<n;i++){int cnt=0;for(int j=0;j<a[i];j++) if(label[j]) cnt++;label[a[i]]=0;ans+=cnt*fac[n-i-1];}return ans;
}
  • 切记,根据公式推导出来的康拓展开序号是从0开始算的!!!

别以为只需要套模板就能过上面那道题,当排列长度巨大,阶乘也会变得巨大,所以得用高精优化的康拓!!


CODE专区

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
char x[1001];
int N=0,num[1001]={0},f[61][101]={0},a[1001]={0},ans[1001]={0};字符X数组用来存储输入进来的数列,包括空格NUM数组用来存从字符X中提取出来的数字F数组用来存阶乘,F[X]表示X的阶乘A数组用来存和公式一样的:当前数是第几小ANS数组用来存最后累加后的康拓答案 void mul(int x,int y,int n)  // 高精乘
{int g=0,s=0;for(int i=100;i>=0;i--){s=f[x][i]*n+g;f[y][i]=s%10;g=s/10;}
}void add(int x)  //高精加
{int g=0,s=0;f[0][100]=1;for(int i=100;i>=0;i--){s=ans[i]+f[x][i]+g;ans[i]=s%10;g=s/10;}
}void input()
{gets(x);for(int i=0;i<strlen(x);i++) //在输入的字符数组中提取数字存入NUM中 {if(x[i]==' ') continue;N++;int j=i;while(x[j]>='0'&&x[j]<='9'){num[N]=num[N]*10+(x[j]-'0');  //数位分离提取数字存入NUM数组中j++; }i=j;  }f[0][100]=1;  //切记!!0!表示1!!!for(int i=1;i<=N;i++) mul(i-1,i,i); //用高精乘算阶乘
}void sortt()  //求从1到N每个数分别是第几小
{for(int i=1;i<=N;i++){for(int j=i+1;j<=N;j++)if(num[j]<num[i])a[i]++;}
}void cangtuo()
{int t=N-1;for(int i=1;i<=N;i++)  //模拟康拓展开的计算公式 {mul(t,t,a[i]);add(t);t--;}int i=0;while(ans[i]==0&&i<101) i++;  //输出 for(int len=i;len<=100;len++) cout<<ans[len];
}int main()
{input();sortt();cangtuo();return 0;
}

此为AC代码此为AC代码 此为AC代码

总结

康拓展开在实际运用的时候一般是不需要用到高精度的(除非题目数据毒瘤),因此代码量不会很大,可以如同高精一样,作为一种工具运用在程序里面。

但,了解了这么多有关康拓展开的东西,它到底有什么值得运用的呢?似乎只能求排列序号而已吧?
有这种想法的人可以看看这道题:八数码

怎样,是不是题目咋一看很简单,其实并不容易:因为,如果要记录下八数码棋盘中的状态,不用任何压缩的话就要耗费很大的内存空间,这对巨佬来说是无法忍受的。但是,通过题目我们不难发现,八数码棋盘的每一种状态中的数字,在当前状态都只会出现一次——这不就是排列吗?

是的,八数码问题的所有状态就是对0 1 2 3 4 5 6 7 8这9个数的全排列

知道了这一点,我们就不必耗费大量的空间存储状态,直接以时间换空间,用一个康拓算法求出当前状态在排列中的编号,然后用来存储——秒变空间压缩大师。

由此可见,哪怕再奇葩的算法和公式,都有他能发挥作用的时候,而对于康拓展开,我认为这绝对是一件空间压缩神器。

当然,如果真的想用康拓算法来进行空间压缩,那么总要提取吧?那么就得用到和康拓展开成对的一个算法——“康拓逆展开”来进行将康拓展开后的数据化为数列。


拓展:

康托逆展开就是知道排名,求出当前数列

首先,把排名转化为以0为开始的排名【就是自减1】

举个例子吧:

对于1,2,3,4,5,求第10的数列
10-1=9
第一个数:9/(5-1)!=0…9,所以第一个数是当前未出现的第0个数:1
第二个数:9/(4-1)!=1…3,所以第二个数是当前未出现的第1个数:3
第三个数:3/(3-1)!=1…1,所以第二个数是当前未出现的第1个数:4
第四个数:1/(2-1)!=1…0,所以第二个数是当前未出现的第1个数:5
第五个数:0/(1-1)!=0…0,所以第二个数是当前未出现的第0个数:2
就这样,第十数列就是1,3,4,5,2

void codel(int x){int cnt;for(int i=0;i<m;i++) label[i]=1;for(int i=0;i<m;i++){cnt=x/fac[m-1-i];x=x%fac[m-1-i];for(int j=0;j<m;j++){if(!label[j]) continue;if(!cnt) {label[j]=0;n[i]=j;break;}cnt--;}}
}

为爆肝爱好者准备的康拓展开练习题:

https://www.luogu.com.cn/problem/P5367

P1377 编码II (康拓展开)相关推荐

  1. 九宫重排_康拓展开_bfs

    历届试题 九宫重排   时间限制:1.0s   内存限制:256.0MB 问题描述 如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着.与空格子相邻的格子中的卡片可以移动到空格中.经 ...

  2. 【康拓展开逆康托展开】

    百度百科就够了 自己的体会: 康托展开是基于比他小的前面的个数来进行计算的        另外康托展开也是一个数组到一个数的映射,因此也是可用于hash,用于空间压缩.比如在保存一个序列,我们可能需要 ...

  3. 八数码(康拓展开标记)及类似题

    文章:https://www.cnblogs.com/Mychael/p/8282895.html 康拓展开,知道数列求排名 //3 2 5 4 1 59 #include<bits/stdc+ ...

  4. 康拓展开/全排列散列(俞勇板子)

    首先可以参考这一篇博文传送门,这篇博文说的很好,大多数康拓展开都是这样写的,但是俞老师的板子是正好反着进行判断一次,不过也是很好的,附上俞老师的注释与板子 任务:对一个N的全排列,返回一个整数代表它在 ...

  5. 【蓝桥杯】九宫重排(BFS+康拓展开)

    问题描述 如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着.与空格子相邻的格子中的卡片可以移动到空格中.经过若干次移动,可以形成第二个图所示的局面. 我们把第一个图的局面记为:12 ...

  6. 九宫重拍(bfs + 康拓展开)

    问题描述 如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着.与空格子相邻的格子中的卡片可以移动到空格中.经过若干次移动,可以形成第二个图所示的局面. 我们把第一个图的局面记为:12 ...

  7. 康拓展开即逆康拓展开(简单易懂版)

    康拓展开 当我们去搜康托展开这个关键字的时候,映入眼帘的是下面的一大堆公式: 其中X为康拓展开值 为整数,且 且表示袁数在当前未出现的元素是排第几个. 很不错,这样一下子会把人弄得搞陀不清(湖南方言) ...

  8. 康拓展开 逆康拓展开

    题目:给出n个互不相同的字符, 并给定它们的相对大小顺序,这样n个字符的所有排列也会有一个顺序.?现在任给一个排列,求出在它后面的第i个排列.这是一个典型的康拓展开应用,首先我们先阐述一下什么是康拓展 ...

  9. 康拓展开和逆康拓展开

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

最新文章

  1. 欧拉公式——真正的宇宙第一公式
  2. java读取配置文件的几种方法
  3. 关于“数据可视化思考者”的8条军规
  4. 量化指标公式源码_通达信实用指标:《看涨跌》指标公式源码
  5. 【强化学习】DQN及其延伸算法
  6. 将文件从HDFS复制到本地
  7. 像素纵横比基础知识(正方形像素,非正方形像素)
  8. 01.Python基础_菜单_快捷键_基本语法_变量_输入输出
  9. 线程与进程的区别_Java线程和PC进程的区别
  10. 实战+练习!用机器学习预测肺癌 | 明晚8点美国西奈山博后免费直播
  11. php.ini 中文详解
  12. github api常用操作
  13. 情爱难剪断,菩提树下悟佛缘
  14. java文件上传控件_java实现大文件上传控件
  15. oracle 批量给字段加注释,Oracle给表和字段添加注释
  16. 笔记:Gitlab-CI部署流程
  17. 几百万消息在消息队列里积压了几个小时!完了。。。
  18. C++--第1课 - C到C++的升级
  19. Linux安装GCC方法—简洁版
  20. smart3D的初探索

热门文章

  1. Spring Boot项目打包Docker镜像
  2. 转载:史上最全|阿里那些牛逼带闪电的开源工具,你知道几个?
  3. TP-LINK150M无线USB网卡免驱版详细配置指南
  4. 非域环境修改域用户密码一直提示你的新密码不符合域的长度、复杂性或历史记录要求。请尝试选择其他新密码。
  5. 水果店如何布局微信小程序让销售额激增
  6. PHP5.6连接数据库和一些简单操作
  7. 密码算法是安全的吗?
  8. Win7语言栏不见了
  9. ENIGMA密码机程序
  10. IEEE Trans 2008 Gradient Pursuits论文学习