全排列散列
来源:互联网 发布:弗洛伊德算法模拟 编辑:程序博客网 时间:2024/06/07 10:12
- 注:代码很长,不要害怕,不难的,核心代码只有几行,贴上完整代码是为了大家测试方便。
首先明确我们要求的是什么样的题目。
例如,给定数组a[10] = {1,2,3,4,5,6,7,8,9,10};
我们把排列{1,2,3,4,5,6,7,8,9,10}规定为0
我们把排列{1,2,3,4,5,6,7,8,10,9}规定为1
......
现给定排列{2,3,5,1,4,6,8,7,9,10} 代表的是多少?
当然,我们可以通过递归求解a数组的全排列,并且计数并判断当前全排列是否是要求的全排列,如果是,那么输出结果。
我们知道当有n个数的时候他的全排列数字是n!个,当n较大时,通过递归(或者使用全排列函数)去枚举,时间效率是非常低的。那么怎么办呢?
有一个神奇的公式叫做康托展开式:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,
其中a[i]为当前未出现的元素中是排在第几个(从0开始)。
那么我们通过模拟就可以很快的写出代码:
#include <cstdio> #include <iostream>#include <map>#include <set>#include <vector>#include <cmath>#include <string>#include <cstring>#include <algorithm>#define LL long long#define MAXN 1000using namespace std;/*全排列散列 康托展开 */// 求阶乘 LL fac(int x){LL ans = 1;for(int i = 2; i <= x; i++){ans *= i; }return ans; }LL ans;int n;int a[100];void arrayToInt(){for(int i = 0; i < n; i++){int temp = 0;for(int j = i+1; j < n; j++){if(a[j] < a[i]) temp++; }ans += temp * fac(n-i-1); }printf("%lld\n",ans);} int main(){//freopen("input.txt","r",stdin);scanf("%d",&n);for(int i = 0; i < n; i++){scanf("%d",&a[i]);}arrayToInt();return 0;}
既然我们可以通过全排列求出这个全排列是第几个,
那么,如果我们知道某个全排列是第几个,是否也可以求出这个全排列是什么样的呢?
那我们将上面的公式倒推一下就可以。
叫做逆康托展开式(也有的称为康托逆展开式)。
即我们知道了X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!其中X 知道 求解a[ 1 ] - a[ n ]
注:X是从0开始
这里我们举个例子来说明:
已知序列1 2 3 4求编号为 3 的序列是什么(1 3 4 2)
3 / 3! = 0 余 3
3 / 2! = 0 余 3
3 / 1! = 3 余 0
0 / 0! (这里不操作)
(上一个算式的余数是下一个算式的被除数,每个算式的商是对应的a数组中算数)
由上可知,
比第一个数小的数有0个,
比第二个数小的有0个,
比第三个数小的有3个
即a[ 4 ] = 0, 所以第一个数是1
a[ 3 ] = 0, 因此1已经用过,所以第二个数是2a[ 2 ] = 3,
所以第三个数是4a[ 3 ] 最后剩下2 ,
所以第四个数字是2
同样的,我们只需要模拟算法的过程即可得到代码:
#include <cstdio> #include <iostream>#include <map>#include <set>#include <vector>#include <cmath>#include <string>#include <cstring>#include <algorithm>#define LL long long#define MAXN 1000using namespace std;/*全排列散列 康托展开 */// 求阶乘 LL fac(int x){LL ans = 1;for(int i = 2; i <= x; i++){ans *= i; }return ans; }LL ans;int n;int a[100];int used[100];void intToArray(int x){memset(used, 0, sizeof(used));int i, j, temp;for(i = 0; i < n; i++){temp = x / fac(n-i-1); for(j = 1; j <= n; j++){if(!used[j]){if(temp == 0) break;temp --;}}a[i] = j; used[j] = 1;x %= fac(n-i-1);}for(int i = 0; i < n; i++){printf("%d ",a[i]);}} int main(){freopen("input.txt","r",stdin);scanf("%d",&n);for(int i = 0; i < 10; i++){intToArray(i); puts("");}return 0;}
康托展开是个特殊的哈希函数,当然他还有别的应用,,比如在搜索问题中,我们可以对vis数组进行压缩,如八数码问题。
- 全排列散列
- 全排列散列
- 全排列散列
- 全排列散列,康拓展开
- 全排列的随机散列的实现(VB2005)
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- 全排列
- python3之编写ftp爆破
- 欢迎使用CSDN-markdown编辑器
- 什么是UFT(QTP)?
- HBase 事务和并发控制机制原理
- 坚持#第174天~昨天写天天吃货写到凌晨1点啦~辛德勒、珍惜
- 全排列散列
- RMQ (Range Minimum/Maximum Query)算法(转)
- Redis 字符串(String)
- 腾讯云上Selenium用法示例
- Codevs 1576 最长严格上升子序列
- 并查集详解 (转) 杭电HDU1232畅通工程
- leetcode287. Find the Duplicate Number
- 修改一段话里指定位置的字的颜色
- AngularJS的指令