您的位置:首页 > 其它

康托展开和康托展开的逆运算

2012-11-09 16:12 288 查看
八数码问题不用康托展开判断重复8s,用康托展开判断重复30MS。康托展开最大最明显的作用就是在判断状态是否重复方面了,其实属于hash的一个技巧。



一、康托展开
【问题背景】对于一个有n个不同元素的集合{1,2,3,4,...,n}的从小到大排序(从大到小 同理)的全排列 显然它有n!项。如n=4,那么就有4!=4×3×2×1=24项。

与自然数1,2,3,4,-----n!与之一一对应。比如 1~4四个数的全排列按字典序如下:

1234:第一个

1243:第二个

1324:第三个

1342:第四个

1423:第五个

1432:第六个



2134:第七个

2143:第八个

2314:第九个

2341:第十个

2413:第11个

2431:第12个



3124:第13个

3142:第14个

3214:第15个

3241:第16个

3412:第17个

3421:第18个

4123:第19个

4132:第20个

4213:第21个

4231:第22个

4312:第23个

4321:第24个

【主要问题】

例1:求4132是第几个排列? 看上面就知道答案就是:20。 那么是怎么算的呢?

解:总共4个数,所以n=4.ans:=0;

第一个数是4,研究比4小的并且还没有出现过的数有3个:1,2,3。那么ans:=ans+3*(n-1)!

所以 ans:= ans+ 3* 3*2*1 =18

第二个数是1,研究比1小的并且还没有出现过的数为 0个。那么ans:=ans+0 * (n-2)!,那么ans不变。

第三个数是3,研究比3小的并且还没有出现过的数为1个:1,2。那么ans:=ans+ 1* (n-3)!,那么ans:=18+1* 1=19

第四个数是2,研究比2小的并且还没有出现过的数为0个。那么ans不变。其实最后一个可以不研究了,比它大和比它小的全都出现过了。最后ans怎么等于19啊??代表它前面有19个排列嘛,那么4132自己就是第20个罗( 最后ans:=ans+1)

例2:问45231是第几个排列?

4 5 2 3 1

ans:= 3*4! + 3*3! + 1*2! + 1*1! + 0*0! + 1 =94

练习

第一题:找出35142在1~5从小到大全排序中的位置?

第二题:找出6482731在1~8从小到大全排列中的位置?

第三题:找出35142在1~5从大到小全排序中的位置?

第四题:找出6482731在1~8从大到小全排列中的位置?


康托展开

<span style="font-size:18px;">  int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
  int cantor(int[] a, int k) {
  int i, j, tmp, num = 0;
  for (i = 0; i < k; i++) {
  tmp = 0;
  for (j = i + 1; j < k; j++)
  if (a[j] < a[i])tmp++;
  num += fac[k - i - 1] * tmp;
  }
  return num;
  }</span>


二、康托展开的逆运算
例3:1~5从小到大全排列中,找出第96个排列?

解:首先设x1x2x3x4x5, (x1等于?不知道),用96-1得到95,表示x1x2x3x4x5前面有95个序列。

第一个数x1,假设x1目前有k个比x1小的并且还没有出现过的数,那么

k:= 95 div (n-1)! = 95 div24=3, 也就是有3个比x1小并且没有出现过的数,那么x1=4.

95变成95-3×24=23

第二个数x2,假设x2目前有k个比x2小的并且还没有出现过的数,那么

k:= 23 div (n-2)! = 23 div 6= 3, 也就是有3个比x2小并且没有出现过的数,那么x2=5.(有3个数比它小的数是4,但4已经在之前出现过了,所以是5)

23变成 23 – 3 * 6 = 5

第三个数用23去除3! 得到3余5

第四个数用5去除2!得到2余1

第五个数就是最后还没有出现的那个。

所以这个数是45321

练习:

第五题:8个数从小到大排序,求第28017个排列?

第六题:8个数从大到小排序,求第14302个排列?

注意 8!= 42320


逆康托展开:

<span style="font-size:18px;">  int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
  int[] uncantor(int x, int k) {
  int res[] = new int[9];
  int i, j, l, t;
  boolean h[] = new boolean[12];
  for (i = 1; i <= k; i++) {
  t = x / fac[k - i];
  x -= t * fac[k - i];
  for (j = 1, l = 0; l <= t; j++)
  if (!h[j])l++;
  j--;
  h[j] = true;
  res[i - 1] = j;
  }
  return res;
  }</span>


http://10.3.20.223/JudgeOnline/showproblem?problem_id=1038 noip2004普及火星人

http://10.3.20.223/JudgeOnline/showproblem?problem_id=11488数码问题

http://10.3.20.223/JudgeOnline/showproblem?problem_id=1209魔板ioi96

http://10.3.20.223/JudgeOnline/showproblem?problem_id=1212巧妙取量
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: