hdu-1027-Ignatius and the Princess II

Ignatius and the Princess II

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11183    Accepted Submission(s): 6435

 

Problem Description

Now our hero finds the door to the BEelzebub feng5166. He opens the door and finds feng5166 is about to kill our pretty Princess. But now the BEelzebub has to beat our hero first. feng5166 says, "I have three question for you, if you can work them out, I will release the Princess, or you will be my dinner, too." Ignatius says confidently, "OK, at last, I will save the Princess."

"Now I will show you the first problem." feng5166 says, "Given a sequence of number 1 to N, we define that 1,2,3...N-1,N is the smallest sequence among all the sequence which can be composed with number 1 to N(each number can be and should be use only once in this problem). So it's easy to see the second smallest sequence is 1,2,3...N,N-1. Now I will give you two numbers, N and M. You should tell me the Mth smallest sequence which is composed with number 1 to N. It's easy, isn't is? Hahahahaha......"
Can you help Ignatius to solve this problem?

Input

The input contains several test cases. Each test case consists of two numbers, N and M(1<=N<=1000, 1<=M<=10000). You may assume that there is always a sequence satisfied the BEelzebub's demand. The input is terminated by the end of file.

Output

For each test case, you only have to output the sequence satisfied the BEelzebub's demand. When output a sequence, you should print a space between two numbers, but do not output any spaces after the last number.

Sample Input

6 4

11 8

Sample Output

1 2 3 5 6 4

1 2 3 4 5 6 7 9 8 11 10

Author

Ignatius.

 

题目大意:给定一个从1到N的序列,求出第M小的序列。

思路:可以用逆康拓展开式和next_permutation()函数去做。本人用的是第二种方法,如果有不清楚康拓展开式的,后面会科普下。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[1001];
int main() {
  int n, m;
  while (scanf("%d%d", &n, &m) != EOF) {
    int k = 1;
    for (int i = 0; i < n; i++)
      a[i] = i+1;
    do {
      if (k > m-1) { //如果序列到了第M小输出这个序列,然后退出
        for (int i = 0; i < n; i++) {
          if (i != 0) printf(" ");
          printf("%d", a[i]);
        }
        printf("\n");
        break;
      }
      k++;
    } while (next_permutation(a, a+n));
  }
}

康拓展开运算:

其中, ai为整数,并且  。ai表示原数的第i位在当前未出现的元素中是排在第几个

举个例子说明。
  在 (1,2,3,4,5)  5个数的排列组合中,计算 34152的康托展开值。首位是3,则小于3的数有两个为1和2,a[5] = 2 .

第二位是4,则小于4的数有两个,为1和2,注意这里3并不能算,因为3已经在第一位,所以其实计算的是在第二位之小于4的个数。因此  a[4] = 2. 第三位是1,则在其之后小于1的数有0个,所以 a[3] = 0 第四位是5,则在其之后小于5的数有1个,为2,所以a[2] = 1
最后一位就不用计算啦,因为在它之后已经没有数了,所以  固定为0
根据公式:

       X = 2*4! + 2*3! + 0*2! + 1*1! + 0*0!
  所以比34152小的组合有61个,即34152是排第62。

康托展开的逆运算

既然康托展开是一个双射,那么一定可以通过康托展开值求出原排列,即可以求出n的全排列中第x大排列。

    一开始已经提过了,康托展开是一个全排列到一个自然数的双射,因此是可逆的。即对于上述例子,在(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。

最后一位自然就是剩下的数2。

通过以上分析,所求排列组合为 34152。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值