输出n的全排列的字典序编号为k的全排列

n个元素{1,2,•••,n}有n!个不同的排列。将这n!个排列按字典序排列。并编号为0,1,2.....,n!-1。每 个排列的编号为其字典序的值。例如。当n=3时,其字典排序为:123,132,213,232,312,321,这六个数的字典序值分别为 0,1,2,3,4,5,现给定任意n,输出字典序为k的排列(0<=k<=n!-1)。

这题 也是来源于今年的美团面试题..

一开始 我也无从下手..

后来 看了http://www.cnblogs.com/submarine/archive/2010/04/12/1941268.html

这边 关于 编号的定义 就差不多明白了

2 6 4 5 8 1 7 3

看例子:

tot=0;

比2小的数有1个,则 tot+=1*7!;

比6小的数有4个,则 tot+=4*6!;

比4小的数有2个,则 tot+=2*5!;

比5小的数有2个,则 tot+=2*4!;

比8小的数有3个,则 tot+=3*3!;

比1小的数有0个,则 tot+=0*2!;

比7小的数有1个,则 tot+=1*1!;

比3小的数没有;

(注:在排列中,求比某个数小的数的个数时,排除之前出现过的数)

这是来源于他的博客 介绍  明白了就好

下面是我的代码  自己测试的几组数据都已通过.

 1 #include <iostream>
 2 #include <cstring>
 3 using namespace std;
 4
 5 const int size = 1010;
 6 typedef long long LL;
 7 LL fact[size];
 8 bool vis[size];
 9 void init( )
10 {
11     fact[0] = 1;
12     for( int i = 1 ; i<size ; i++ )
13     {
14         fact[i] = fact[i-1] * i;
15     }
16 }
17
18 int main()
19 {
20     LL n , k , t , mmin , cnt , temp;
21     init( );
22     while( cin >> n >> k )
23     {
24         mmin = 1;
25         memset( vis , false , sizeof(vis) );
26         for( int i = 1 ; i<=n ; i++ )
27         {
28             if( k==0 )
29             {
30                 for( int j = mmin ; j<=n ; j++ )
31                 {
32                     if( !vis[j] )
33                     {
34                         cout << j << " ";
35                     }
36                 }
37                 break;
38             }
39             else if( k<fact[n-i] )
40             {
41                 while(1)
42                 {
43                     if( !vis[mmin] )
44                     {
45                         vis[mmin] = true;
46                         cout << mmin << " ";
47                         break;
48                     }
49                     ++mmin;
50                 }
51             }
52             else
53             {
54                 cnt = 0;
55                 t = k / fact[n-i];
56                 k -= t * fact[n-i];
57                 temp = mmin;
58                 ++ t;
59                 while(1)
60                 {
61                     if( !vis[temp] )
62                     {
63                         ++ cnt;
64                     }
65                     if( cnt == t )
66                     {
67                         vis[temp] = true;
68                         cout << temp << " ";
69                         break;
70                     }
71                     ++ temp;
72                 }
73             }
74         }
75         cout << endl;
76     }
77     return 0;
78 }

先折叠起来 大家可以写完后 和我 对拍下

today:

  从吹牛b到懂得沉默 就开始慢慢成熟了

时间: 2024-12-29 09:04:12

输出n的全排列的字典序编号为k的全排列的相关文章

全排列与字典序排列

首先,全排列是一个比较简单的问题,但我却没有真正的去实现过全排列. 让我独自思考全排列的话,如将 " abcd" 进行全排列,这种简单的全排列也能将我难住,因为真的没有考虑过这种问题.思考了一会,我只能给出以下比较麻烦的算法: //字符串全排列 void printRE(char* str,int index,char s[],int length){ if(index == length) printf("%s \n",str); else{ bool exsis

poj 2337 欧拉回路按照最小字典序输出+注意为了按最小字典序怎么处理邻接表

http://poj.org/problem?id=2337 WA了好久,昨晚1点多睡不着写的,狂WA,当时是因为用邻接矩阵存储,比如aba,aa只能存下一个,这个之前还没遇到过,今天才注意到--邻接矩阵无法存储平行边, 关于欧拉回路判断看我另几篇日志或者看我的欧拉总结 再贴个输出欧拉回路的模板 其中,参数u是起点,注意如果是输出欧拉路径的话,u必须是出度比入度大一的那个点,如果输出欧拉回路,随便按要求找个就行 void euler(int u) { for(int i=head[u];i!=-

全排列算法(字典序法、SJT Algorithm 、Heap&#39;s Algorithm)

一.字典序法 1) 从序列P的右端开始向左扫描,直至找到第一个比其右边数字小的数字,即. 2) 从右边找出所有比大的数中最小的数字,即. 3) 交换与. 4) 将右边的序列翻转,即可得到字典序的下一个排列. 5) 重复上面的步骤,直至得到字典序最大的排列,即左边数字比右边的大的降序排列. 二.SJT Algorithm 初始状态为. 1) 找到最大的可移动数m(当一个数指向一个比它小的数是,该数就是可移动数) 2) 交换m和m所指向的数 3) 改变所有比m大的数的方向 4) 重复上面的步骤,直至

生成全排列的字典序算法

字典序算法如下: 设P是1-n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn 1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1} 2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=min{pk|pk>pj,k>i} 3)对换pi,pk 4)再将pj+1......pk-1pkpk+1pn倒转得到排列p’’=p1p2.

全排列 未按字典序

思想:从集合中依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理,从而得到所有元素的全排列 #include <stdio.h> /************************************************************************/ /* 功能:实现两个整形参数值交换 /* 参数: /*       lhs--int类型的指针,指向待交换数1的地址 /*       rhs--int类型的指针,指向待交换数2的地址

2014年美团校招之——输出字典序为第k的排列(0&lt;=k&lt;n!)

思路: 比如:n=4,k=6(k从0开始计数),那么就是从找第四个数,那么看规律 (第一队) 1234 1243 1324 1342 1423 1432 (第二队) 2134 2143 2314 2341 2413 2431 (第三队) .... 我们从第一个数字开始确定,由于确定第一个数了,那么后面的排列组合数是(n-1)!也就是6.问题就是如何确定第一个数,这里k=6,我们算出 k/n=1,说明这个数在第二队中. 那么我们应该把2移到最前面,此时排列就变成了2134.此时问题变为在第二队中找

Lexicography(数学推论&gt;&gt;求按字典序排第k个排列)

Lexicography Time Limit:1000MS     Memory Limit:131072KB     64bit IO Format:%lld & %llu Submit Status Practice CSU 1563 Description An anagram of a string is any string that can be formed using the same letters as the original. (We consider the orig

开灯问题--------《算法竞赛入门指导》P83

开灯问题. 有n盏灯,编号为1-n.第1个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依此类推.一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号.k≤n≤1000.样例输入:7 3样例输出:1 5 6 7 我的代码为 #include<iostream> using namespace std; int main(void){ int n,k; cin>>n;

开灯和蛇形

竞赛初入门,发现题目是真的挺难的,一道题目看下来完全不知道在说什么,或者是没头绪,看了答案之后才慢慢能理解,嘛,一步一步来吧. 开灯问题,有n盏灯,编号为1-n, 第一个人把所有的灯都打开,第二个人按下所有编号为2的倍数的开关(这些灯将被关掉),第三个人按下所有编号为3倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),以此类推,一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号,k <= n <= 1000. 样例输入: 7 3 样例输出: 1 5 6 7 分析:这里直接用双